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

RShiny:如何在fluidRow()内创建3个小方块作为plot_ly堆叠柱状图图例

实现自定义独立图例的方案

没问题!要实现把三个独立的图例方块放在plotly堆叠柱状图下方的fluidRow里,完全替代plotly自带的图例,咱们可以用Shiny的原生标签来构建自定义图例,同时确保颜色和图表完全匹配。下面是具体的实现方案:

基础版:静态自定义图例

这个方案先关闭plotly自带的图例,然后在下方的fluidRow里手动创建三个带颜色方块的图例项,和图表颜色严格对应:

library(shiny)
library(plotly)

# 统一定义堆叠柱的颜色,确保图例和图表颜色一致
bar_colors <- c("#FF6B6B", "#4ECDC4", "#45B7D1")

ui <- fluidPage(
  # 堆叠柱状图区域
  plotlyOutput("stacked_bar"),
  
  # 自定义图例的fluidRow,三个图例项平分宽度
  fluidRow(
    column(
      width = 4,
      tags$div(style = "display: flex; align-items: center; justify-content: center; margin-top: 10px;",
               # 彩色方块
               tags$div(style = paste0("width: 22px; height: 22px; background-color: ", bar_colors[1], "; border-radius: 2px; margin-right: 8px;")),
               # 图例文字
               tags$span("类别1", style = "font-size: 14px;")
      )
    ),
    column(
      width = 4,
      tags$div(style = "display: flex; align-items: center; justify-content: center; margin-top: 10px;",
               tags$div(style = paste0("width: 22px; height: 22px; background-color: ", bar_colors[2], "; border-radius: 2px; margin-right: 8px;")),
               tags$span("类别2", style = "font-size: 14px;")
      )
    ),
    column(
      width = 4,
      tags$div(style = "display: flex; align-items: center; justify-content: center; margin-top: 10px;",
               tags$div(style = paste0("width: 22px; height: 22px; background-color: ", bar_colors[3], "; border-radius: 2px; margin-right: 8px;")),
               tags$span("类别3", style = "font-size: 14px;")
      )
    )
  )
)

server <- function(input, output) {
  output$stacked_bar <- renderPlotly({
    # 示例数据
    df <- data.frame(
      x = c("分组A", "分组B", "分组C"),
      y1 = c(12, 25, 18),
      y2 = c(18, 20, 22),
      y3 = c(8, 12, 9)
    )
    
    # 绘制堆叠柱状图,关闭自带图例
    plot_ly(df, x = ~x) %>%
      add_bars(y = ~y1, name = "类别1", marker = list(color = bar_colors[1])) %>%
      add_bars(y = ~y2, name = "类别2", marker = list(color = bar_colors[2])) %>%
      add_bars(y = ~y3, name = "类别3", marker = list(color = bar_colors[3])) %>%
      layout(
        barmode = "stack",
        showlegend = FALSE, # 关闭plotly自带图例
        xaxis = list(title = "分组"),
        yaxis = list(title = "数值")
      )
  })
}

shinyApp(ui, server)

关键细节说明:

  • 用bar_colors向量统一管理颜色,避免图表和图例颜色不一致;
  • 通过layout(showlegend = FALSE)彻底关闭plotly自带的图例;
  • 用fluidRow+column实现图例的横向排列,每个列里用flex布局让彩色方块和文字垂直对齐;
  • 可以调整方块的width/height/border-radius来改变图例方块的样式,比如改成圆形或者更大的尺寸。

进阶版:可交互的自定义图例

如果需要点击图例来显示/隐藏对应堆叠柱,可以结合shinyjs包实现交互效果:

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

bar_colors <- c("#FF6B6B", "#4ECDC4", "#45B7D1")

ui <- fluidPage(
  useShinyjs(), # 初始化shinyjs
  plotlyOutput("stacked_bar"),
  
  fluidRow(
    column(
      width = 4,
      tags$div(
        id = "legend1",
        style = "display: flex; align-items: center; justify-content: center; margin-top: 10px; cursor: pointer;",
        tags$div(style = paste0("width: 22px; height: 22px; background-color: ", bar_colors[1], "; border-radius: 2px; margin-right: 8px;")),
        tags$span("类别1", style = "font-size: 14px;")
      )
    ),
    column(
      width = 4,
      tags$div(
        id = "legend2",
        style = "display: flex; align-items: center; justify-content: center; margin-top: 10px; cursor: pointer;",
        tags$div(style = paste0("width: 22px; height: 22px; background-color: ", bar_colors[2], "; border-radius: 2px; margin-right: 8px;")),
        tags$span("类别2", style = "font-size: 14px;")
      )
    ),
    column(
      width = 4,
      tags$div(
        id = "legend3",
        style = "display: flex; align-items: center; justify-content: center; margin-top: 10px; cursor: pointer;",
        tags$div(style = paste0("width: 22px; height: 22px; background-color: ", bar_colors[3], "; border-radius: 2px; margin-right: 8px;")),
        tags$span("类别3", style = "font-size: 14px;")
      )
    )
  ),
  
  # 定义隐藏状态的样式
  tags$style(
    ".inactive div { opacity: 0.4 !important; }",
    ".inactive span { color: #999 !important; }"
  )
)

server <- function(input, output, session) {
  # 存储每个trace的可见状态
  visible_state <- reactiveValues(
    trace1 = TRUE,
    trace2 = TRUE,
    trace3 = TRUE
  )
  
  output$stacked_bar <- renderPlotly({
    df <- data.frame(
      x = c("分组A", "分组B", "分组C"),
      y1 = c(12, 25, 18),
      y2 = c(18, 20, 22),
      y3 = c(8, 12, 9)
    )
    
    plot_ly(df, x = ~x) %>%
      add_bars(y = ~y1, name = "类别1", marker = list(color = bar_colors[1]), visible = visible_state$trace1) %>%
      add_bars(y = ~y2, name = "类别2", marker = list(color = bar_colors[2]), visible = visible_state$trace2) %>%
      add_bars(y = ~y3, name = "类别3", marker = list(color = bar_colors[3]), visible = visible_state$trace3) %>%
      layout(barmode = "stack", showlegend = FALSE)
  })
  
  # 监听图例1的点击事件
  observeEvent(input$legend1_click, {
    visible_state$trace1 <- !visible_state$trace1
    toggleClass(id = "legend1", class = "inactive")
  })
  
  # 监听图例2的点击事件
  observeEvent(input$legend2_click, {
    visible_state$trace2 <- !visible_state$trace2
    toggleClass(id = "legend2", class = "inactive")
  })
  
  # 监听图例3的点击事件
  observeEvent(input$legend3_click, {
    visible_state$trace3 <- !visible_state$trace3
    toggleClass(id = "legend3", class = "inactive")
  })
}

shinyApp(ui, server)

进阶功能说明:

  • 点击图例项会切换对应堆叠柱的显示/隐藏,同时图例样式会变灰(通过inactive类实现);
  • 用reactiveValues存储每个trace的可见状态,确保图表能实时更新;
  • shinyjs的toggleClass方法用来切换图例的激活/非激活样式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 07:12:51