1

I am trying to make an animated plot where new traces are introduced, the traces are animated, and the axes are then rescaled. I am having trouble getting these things to work together. A reprex is below. It works except when I have both Animate Traces and Rescale Axis, then the axis rescaling gets reset on every iteration.

Using Proxy Interface in Plotly/Shiny to dynamically change data

https://community.plot.ly/t/how-to-efficiently-restyle-update-modify-plot-containing-frames/5553

https://plot.ly/javascript/plotlyjs-function-reference/

https://plot.ly/javascript/animations/

It's rather hard to follow the Plotly documentation. I couldn't get addFrames, relayout, restyle, react, or update to work for me. I've had the most luck with animate. I would greatly appreciate any help, I've been struggling with this for two weeks already.

# plotly_add_anim13.R
library(shiny)
library(plotly)
library(dplyr)
library(purrr)

ui <- fluidPage(
    checkboxInput("add", "Add Trace", TRUE),
    checkboxInput("animate", "Animate Traces", FALSE),
    checkboxInput("rescale", "Rescale Axis", FALSE),
    plotlyOutput("plot")
)

server <- function(input, output, session){

    my <- reactiveValues(
        fnumber = NA, # frame number
        frame = NA, # frame data list
        ntraces = NA, # number of traces
        xrange = NA # xaxis range
        )

    speed = 1000 # redraw interval in milliseconds

    output$plot <- renderPlotly({
        isolate({
            cat("renderPlotly\n")
            my$fnumber <- 1
            my$ntraces <- 2
            f <- as.character(my$fnumber)
            x <- runif(2)
            y <- rep(runif(1), 2)
            t <- c("A", "B")
            ids0 <- paste0(my$ntraces-2, letters[1:2])
            ids1 <- paste0(my$ntraces-1, letters[1:2])
            my$xrange <- c(0,1)
            # https://community.plot.ly/t/how-to-efficiently-restyle-update-modify-plot-containing-frames/5553
            my$frame <- list(
                            name = f,
                            data = list(
                                list(x=x, y=y, frame=f, ids=ids0, type="scatter", mode="lines", showlegend=FALSE),
                                list(x=x, y=y, frame=f, ids=ids1, type="scatter", mode="text", text=t, showlegend=FALSE)
                            ),
                            traces = as.list(as.integer(c(my$ntraces-2, my$ntraces-1))),
                            layout = list(xaxis=list(range=my$xrange, zeroline=FALSE),
                                          yaxis=list(range=c(0,1), tickmode="array", tickvals=seq(0,1,0.2), ticktext=seq(0,1,0.2)))
                        )
            p <- plot_ly()
            p <- do.call(add_trace, prepend(my$frame$data[[1]], list(p)))
            p <- do.call(add_trace, prepend(my$frame$data[[2]], list(p)))
            p <- do.call(layout, prepend(my$frame$layout, list(p)))
            p <- animation_opts(p, frame=speed, transition=speed)
            p
        })
    })

    proxy <- plotlyProxy("plot", session=session)

    # https://shiny.rstudio.com/reference/shiny/0.14/reactiveTimer.html
    autoInvalidate <- reactiveTimer(speed*2)

    observeEvent(autoInvalidate(), {
        # req(NULL)
        # https://stackoverflow.com/questions/50620360/using-proxy-interface-in-plotly-shiny-to-dynamically-change-data
        # https://community.plot.ly/t/how-to-efficiently-restyle-update-modify-plot-containing-frames/5553
        # https://plot.ly/javascript/animations/#frame-groups-and-animation-modes
        # https://plot.ly/javascript/animations/
        if (input$add){
            cat("add trace\n")
            my$fnumber <- my$fnumber + 1
            my$ntraces <- my$ntraces + 2
            f <- as.character(my$fnumber)
            x <- runif(2)
            y <- rep(runif(1), 2)
            t <- c("A", "B")
            ids0 <- paste0(my$ntraces-2, letters[1:2])
            ids1 <- paste0(my$ntraces-1, letters[1:2])
            my$frame$name <- f
            my$frame$data[[my$ntraces-1]] <- list(x=x, y=y, frame=f, ids=ids0, type="scatter", mode="lines", showlegend=FALSE)
            my$frame$data[[my$ntraces-0]] <- list(x=x, y=y, frame=f, ids=ids1, type="scatter", mode="text", text=t, showlegend=FALSE)
            my$frame$traces <- as.list(as.integer(1:my$ntraces - 1))
            plotlyProxyInvoke(proxy, "addTraces",
                              list(
                                my$frame$data[[my$ntraces-1]],
                                my$frame$data[[my$ntraces-0]]
                                ))
            plotlyProxyInvoke(proxy, "animate",
                              # frameOrGroupNameOrFrameList
                              list(
                                name = my$frame$name,
                                data = my$frame$data,
                                traces = my$frame$traces
                              ),
                              # animationAttributes
                              list(
                                frame=list(duration=0),
                                transition=list(duration=0)
                              )
            )# animate

        }
        if (input$animate){
            cat("animate traces\n")
            my$fnumber <- my$fnumber + 1
            f <- as.character(my$fnumber)
            traces <- 1:my$ntraces - 1
            for (i in seq(0, my$ntraces-2, 2)){
                x <- runif(2)
                y <- rep(runif(1), 2)
                t <- c("A", "B")
                ids0 <- paste0(i, letters[1:2])
                ids1 <- paste0(i+1, letters[1:2])
                my$frame$data[[i+1]] <- list(x=x, y=y, frame=f, ids=ids0, type="scatter", mode="lines", showlegend=FALSE)
                my$frame$data[[i+2]] <- list(x=x, y=y, frame=f, ids=ids1, type="scatter", mode="text", text=t, showlegend=FALSE)
            }
            my$frame$name <- f
            plotlyProxyInvoke(proxy, "animate",
                              # frameOrGroupNameOrFrameList
                              list(
                                name = my$frame$name,
                                data = my$frame$data,
                                traces = my$frame$traces
                              ),
                              # animationAttributes
                              list(
                                frame=list(duration=speed),
                                transition=list(duration=speed)
                              )
            )# animate
        }
        if (input$rescale){
            cat("animate layout\n")
            my$fnumber <- my$fnumber + 1
            f <- as.character(my$fnumber)
            my$xrange <- runif(2)*0.1+c(-0.1,1)
            my$frame$name <- f
            my$frame$layout <- list(xaxis=list(range=my$xrange))
            plotlyProxyInvoke(proxy, "animate",
                              # frameOrGroupNameOrFrameList
                              list(
                                name = my$frame$name,
                                data = my$frame$data,
                                traces = my$frame$traces,
                                layout = my$frame$layout
                              ),
                              # animationAttributes
                              list(
                                frame=list(duration=speed),
                                transition=list(duration=speed)
                              )
            ) # animate
        }
    }) # observeEvent

}

shinyApp(ui, server)


ismirsehregal
  • 30,045
  • 5
  • 31
  • 78
Simon Woodward
  • 1,946
  • 1
  • 16
  • 24
  • 1
    The following example is related showing how to use `restyle` to replace the underlying data set: https://community.plotly.com/t/what-is-the-most-performant-way-to-update-a-graph-with-new-data/639/6?u=fabern – fabern Jul 19 '20 at 23:49

0 Answers0