且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用plotly proxy在plotly中更改迹线标记的颜色,而无需更改标记大小

更新时间:2023-11-22 09:13:22

您可以使用ShinyJS注入custon javascript代码.在这里,我使用一些d3键选择图例项目并更改其大小.据我所知,它非常hacky,但不幸的是,plotly无法提供内部解决方案.

You can inject custon javascript code using shinyJS. Here, i use some d3 to select the legend items and change their size. It is very hacky but unfortunatly, as far as i know, plotly does not provide an internal solution.

library(plotly)
library(shiny)
library(htmlwidgets)
library(colourpicker)
library(shinyjs)

jsCode <- "shinyjs.changelegend = function(){
var paths = d3.select('#plot1').
select('.legend').
select('.scrollbox').
selectAll('.traces').
select('.scatterpts')
.attr('d','M8,0A8,8 0 1,1 0,-8A8,8 0 0,1 8,0Z');}"

ui <- fluidPage(
  tags$script(src = "https://d3js.org/d3.v4.min.js"),
  useShinyjs(),
  extendShinyjs(text = jsCode),
  fluidRow(
    column(8,
           plotlyOutput("plot1")
    ),
    column(2,
           colourpicker::colourInput(inputId = 'markercolor', label = 'X',
                                     palette = "limited", 
                                     showColour = "background", returnName = TRUE),
           selectInput(inputId = 'traceNo', label = 'Trace', choices = c(1:3), selected = 1),
           br(),
           h5('Switch'),
           actionButton(inputId = 'Switch', label = icon('refresh'), style = "color: #f7ad6e;   background-color: white;  border-color: #f7ad6e;
                        height: 40px; width: 40px; border-radius: 6px;  border-width: 2px; text-align: center;  line-height: 50%; padding: 0px; display:block; margin: 2px")
           )
    ),
  tags$div(id = "test")
  )

server <- function(input, output, session) {
  # values <- reactiveValues()


  observeEvent(input$Switch, { 
    plotlyProxy("plot1", session) %>%
      plotlyProxyInvoke("restyle", list(marker = list(color = input$markercolor)), list(as.numeric(input$traceNo)-1))
  })

  observeEvent(input$Switch,{
    js$changelegend()
  })

  output$plot1 <- renderPlotly({
    markersize <- 4
    markerlegendsize <- 20
    colors <- c('red', 'blue', 'black')
    p1 <- plot_ly()
    p1 <-  add_trace(p1, data = mtcars, x = ~disp, y = ~mpg, type = 'scatter', mode = 'markers', color = ~as.factor(cyl), colors = colors)
    p1 <- layout(p1, title = 'mtcars group by cyl with switching colors')
    p1 <- plotly_build(p1)

    # this is a bit of a hack to change the size of the legend markers to not be equal to the plot marker size.
    # it makes a list of 1 size value for each marker in de trace in the plot, and another half of with sizes that are a lot bigger.
    # the legend marker size is effectively the average size of all markers of a trace
    for(i in seq(1, length(sort(unique(mtcars$cyl) )))) {
      length.group <- nrow(mtcars[which(mtcars$cyl  == sort(unique(mtcars$cyl))[i]), ])
      p1$x$data[[i]]$marker$size <- c(rep(markersize,length.group), rep(c(-markersize+2*markerlegendsize), length.group))
    }
    return(p1)
  })

}

shinyApp(ui, server)

自定义javascript代码在jsCode中定义,并在extendShinyjs()中初始化.最后,只要单击该按钮,就会在js$changelegend()中调用它.

The custom javascript code is defined in jsCode which is initialized in extendShinyjs(). Finally, it is invoked in js$changelegend() whenever the button is clicked.

如果有多个图并且希望具有相同的行为,则可以将图ID作为参数传递到js$changelegend()中,并相应地更改jsCode来进行处理.

If you have multiple plots and you want the same behavior, you can pass the plot id as a parameter into js$changelegend() and change the jsCode accordingly to handle this.