You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调整窗口大小导致Shiny应用中Plotly饼图渲染异常的解决求助

解决R Shiny中Plotly饼图窗口 resize 时渲染异常的问题

问题复现代码

以下是能复现该渲染异常的示例代码:

library(shiny)
library(plotly)
library(bslib)

ui <- page_sidebar(
  sidebar = sidebar(),
  card(
    plotlyOutput("pie1")
  ),
  card(
    plotlyOutput("pie2")
  )
)

server <- function(input, output, session) {
  output$pie1 <- renderPlotly({
    plot_ly(
      labels = c("A", "B", "C"),
      values = c(30, 40, 30),
      type = "pie"
    ) %>%
      layout(margin = list(t = 0, b = 0, l = 0, r = 0))
  })
  
  output$pie2 <- renderPlotly({
    plot_ly(
      labels = c("X", "Y", "Z"),
      values = c(20, 50, 30),
      type = "pie"
    ) %>%
      layout(margin = list(t = 0, b = 0, l = 0, r = 0))
  })
}

shinyApp(ui, server)

可行解决方案

1. 固定容器尺寸+窗口resize时强制重绘

给plotlyOutput设置固定高度和最小高度,避免容器尺寸变化过于极端;同时通过JS监听窗口resize事件,延迟触发Plotly图表重绘,解决渲染异常:

ui <- page_sidebar(
  sidebar = sidebar(),
  # 添加resize监听的JS代码
  tags$script(HTML("
    window.addEventListener('resize', function() {
      // 延迟300ms执行,避免resize过程中频繁触发重绘
      setTimeout(function() {
        const pie1 = document.getElementById('pie1');
        const pie2 = document.getElementById('pie2');
        
        if(pie1 && Plotly.redraw) Plotly.redraw(pie1);
        if(pie2 && Plotly.redraw) Plotly.redraw(pie2);
      }, 300);
    });
  ")),
  card(
    plotlyOutput("pie1", height = "300px", style = "min-height: 250px;")
  ),
  card(
    plotlyOutput("pie2", height = "300px", style = "min-height: 250px;")
  )
)

# server部分保持不变
server <- function(input, output, session) {
  output$pie1 <- renderPlotly({
    plot_ly(
      labels = c("A", "B", "C"),
      values = c(30, 40, 30),
      type = "pie"
    ) %>%
      layout(margin = list(t = 0, b = 0, l = 0, r = 0))
  })
  
  output$pie2 <- renderPlotly({
    plot_ly(
      labels = c("X", "Y", "Z"),
      values = c(20, 50, 30),
      type = "pie"
    ) %>%
      layout(margin = list(t = 0, b = 0, l = 0, r = 0))
  })
}

shinyApp(ui, server)

2. 使用Shiny输入传递resize事件(结合shinyjs)

如果需要在server端控制重绘时机,可以用shinyjs将窗口resize事件转为Shiny输入,触发重绘:

library(shiny)
library(plotly)
library(bslib)
library(shinyjs)

ui <- page_sidebar(
  useShinyjs(),
  sidebar = sidebar(),
  card(
    plotlyOutput("pie1", height = "300px", style = "min-height: 250px;")
  ),
  card(
    plotlyOutput("pie2", height = "300px", style = "min-height: 250px;")
  )
)

server <- function(input, output, session) {
  output$pie1 <- renderPlotly({
    plot_ly(
      labels = c("A", "B", "C"),
      values = c(30, 40, 30),
      type = "pie"
    ) %>%
      layout(margin = list(t = 0, b = 0, l = 0, r = 0))
  })
  
  output$pie2 <- renderPlotly({
    plot_ly(
      labels = c("X", "Y", "Z"),
      values = c(20, 50, 30),
      type = "pie"
    ) %>%
      layout(margin = list(t = 0, b = 0, l = 0, r = 0))
  })
  
  # 监听resize输入,触发重绘
  observeEvent(input$window_resize, {
    runjs("
      setTimeout(function() {
        Plotly.redraw('pie1');
        Plotly.redraw('pie2');
      }, 300);
    ")
  })
  
  # 初始化resize事件监听
  session$onFlushed(function() {
    runjs("
      window.addEventListener('resize', function() {
        Shiny.setInputValue('window_resize', Date.now());
      });
    ")
  })
}

shinyApp(ui, server)

3. 调整Plotly布局参数

开启autosize并设置合理的margin,让Plotly自动适配容器尺寸,减少渲染异常概率:

output$pie1 <- renderPlotly({
  plot_ly(
    labels = c("A", "B", "C"),
    values = c(30, 40, 30),
    type = "pie",
    autosize = TRUE
  ) %>%
    layout(
      margin = list(t = 20, b = 20, l = 20, r = 20),
      autosize = TRUE
    )
})

output$pie2 <- renderPlotly({
  plot_ly(
    labels = c("X", "Y", "Z"),
    values = c(20, 50, 30),
    type = "pie",
    autosize = TRUE
  ) %>%
    layout(
      margin = list(t = 20, b = 20, l = 20, r = 20),
      autosize = TRUE
    )
})

关键说明

  • 不要手动修改SVG的d属性:Plotly的渲染逻辑会在尺寸变化时自动更新SVG属性,手动修改会被覆盖,无法解决根本问题。
  • 延迟重绘的必要性:窗口resize事件会连续触发,设置300ms左右的延迟可以避免频繁重绘导致的性能问题,同时确保容器尺寸稳定后再重绘。

内容的提问来源于stack exchange,提问作者RAU

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 19:07:39