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
相关产品推荐
相关产品推荐

