【问题标题】:How to rotate 3D Plotly continuous for R shiny App如何为 R 闪亮的 App 旋转 3D Plotly 连续
【发布时间】:2021-05-12 14:11:48
【问题描述】:

我正在尝试创建一个恒定旋转的 3D 散点图,以便我可以将其放入我的 R 闪亮应用程序中。但是,我似乎无法让它不断旋转(例如:https://codepen.io/etpinard/pen/mBVVyE)。 我不想将它保存到图像/gif 中,直接在我的应用程序中使用。任何人都可以提供任何帮助以使其不断旋转(我对 Python 几乎没有经验)?我在 R studio 的 Viewer 屏幕上试过这个,但它没有在那里旋转。

library(plotly)
library(ggplot2)


N <- 100

x <- rnorm(N, mean = 50, sd = 2.3)
y <- runif(N,min= 0, max = 100)
z <- runif(N, min = 4, max = 70)
luci.frame <- data.frame(x,y,z)




for (i in seq(0,100, by=0.1)){
  cam.zoom = 2
  ver.angle = 0
  graph <- plot_ly()%>%
    add_trace(type = "scatter3d", 
              mode = "markers", 
              data = luci.frame,
              x = ~x, 
              y = ~y, 
              z = ~z) %>%
    layout(scene = list(
      camera = list(
        eye = list(
          x = cos(i)*cam.zoom,
          y = sin(i)*cam.zoom, 
          z = 0.3
        ), 
        center = list(
          x = 0, 
          y = 0, 
          z = 0
        )
        
      )
      
      
    )
    )
  graph
  
}

我对 plotly 很陌生,所以任何帮助都将不胜感激。

【问题讨论】:

  • 我建议使用 rgl 包。我已经能够做到这一点。如果您以前没有使用过 htmlwidgets,那么会有一点学习曲线。但这应该可以提供您正在寻找的解决方案。
  • 我正在为我的整个应用程序使用 plotly,因为我已经能够添加平面和网格。它产生的互动情节很棒。我绝对需要继续情节。不过谢谢你的推荐。

标签: r animation shiny plotly r-plotly


【解决方案1】:

我们可以通过htmlwidgets::onRender重用大部分JS代码。您标记了问题 - 相应地将其包装在应用程序中:

library(shiny)
library(plotly)
library(htmlwidgets)

ui <- fluidPage(
  plotlyOutput("graph")
)

server <- function(input, output, session) {
  N <- 100
  x <- rnorm(N, mean = 50, sd = 2.3)
  y <- runif(N, min = 0, max = 100)
  z <- runif(N, min = 4, max = 70)
  luci.frame <- data.frame(x, y, z)
  
  output$graph <- renderPlotly({
    plot_ly(
      type = "scatter3d",
      mode = "markers",
      data = luci.frame,
      x = ~ x,
      y = ~ y,
      z = ~ z
    ) %>%
      layout(scene = list(camera = list(
        eye = list(
          x = 1.25,
          y = 1.25,
          z = 1.25
        ),
        center = list(x = 0,
                      y = 0,
                      z = 0)
      ))) %>%
      onRender("
      function(el, x){
  var id = el.getAttribute('id');
  var gd = document.getElementById(id);
  Plotly.update(id).then(attach);
  function attach() {
    var cnt = 0;
    
    function run() {
      rotate('scene', Math.PI / 180);
      requestAnimationFrame(run);
    } 
    run();
    
    function rotate(id, angle) {
      var eye0 = gd.layout[id].camera.eye
      var rtz = xyz2rtz(eye0);
      rtz.t += angle;
      
      var eye1 = rtz2xyz(rtz);
      Plotly.relayout(gd, id + '.camera.eye', eye1)
    }
    
    function xyz2rtz(xyz) {
      return {
        r: Math.sqrt(xyz.x * xyz.x + xyz.y * xyz.y),
        t: Math.atan2(xyz.y, xyz.x),
        z: xyz.z
      };
    }
    
    function rtz2xyz(rtz) {
      return {
        x: rtz.r * Math.cos(rtz.t),
        y: rtz.r * Math.sin(rtz.t),
        z: rtz.z
      };
    }
  };
}
    ")
  })
}

shinyApp(ui, server)


同样可以通过plotlyProxy 完成,无需额外的 JS - 但它并不那么顺利:

library(shiny)
library(plotly)

ui <- fluidPage(
  plotlyOutput("graph")
)

server <- function(input, output, session) {
  N <- 100
  x <- rnorm(N, mean = 50, sd = 2.3)
  y <- runif(N, min = 0, max = 100)
  z <- runif(N, min = 4, max = 70)
  luci.frame <- data.frame(x, y, z)
  
  mySequence <- seq(0, 100, by = 0.1)
  
  cam.zoom = 2
  # ver.angle = 0
  
  output$graph <- renderPlotly({
    plot_ly(
      type = "scatter3d",
      mode = "markers",
      data = luci.frame,
      x = ~ x,
      y = ~ y,
      z = ~ z
    ) %>%
      layout(scene = list(camera = list(
        eye = list(
          x = cos(mySequence[1]) * cam.zoom,
          y = sin(mySequence[1]) * cam.zoom,
          z = 0.3
        ),
        center = list(x = 0,
                      y = 0,
                      z = 0)
      )))
  })
  
  myPlotlyProxy <- plotlyProxy("graph")
  count <- reactiveVal(1L)
  
  observe({
    invalidateLater(100)
    plotlyProxyInvoke(myPlotlyProxy, "relayout", list(scene = list(camera = list(
      eye = list(
        x = cos(mySequence[isolate(count())]) * cam.zoom,
        y = sin(mySequence[isolate(count())]) * cam.zoom,
        z = 0.3
      ),
      center = list(x = 0,
                    y = 0,
                    z = 0)
    ))))
    
    isolate(count(count()+1))
    
    if(count() > length(mySequence)){
      count(1L)  
    }
  })
}

shinyApp(ui, server)

【讨论】:

  • 第一个解决方案看起来很棒,但我无法让它在 Rmarkdown(Hugo 网站)应用程序中工作。还有什么我应该注意的吗?
  • 我建议发布一个单独的问题。没有可重复的例子就很难提供帮助。
猜你喜欢
  • 2021-12-31
  • 2020-06-03
  • 2017-10-31
  • 1970-01-01
  • 2020-09-30
  • 1970-01-01
  • 2020-01-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多