如何在bslib应用的明暗模式中确定前景与背景颜色?
问题描述
我正在试验bslib的明暗模式功能,希望根据明暗模式重绘R基础图形,具体是设置par()的fg和bg参数。
通过搜索找到bs_current_theme和bs_get_variables函数:
current_theme <- bslib::bs_current_theme() bg <- bslib::bs_get_variables(current_theme, "body-bg")[["body-bg"]]
但调用toggle_dark_mode()后,bg的值似乎并未改变。以下是每次点击明暗模式按钮时打印bg值的示例应用:
ui <- bslib::page_navbar( title = "Dark Mode Demo", theme = bslib::bs_theme(version=5), bslib::nav_panel( title = "Base Graphics", bslib::card( bslib::card_header("Mosaic"), shiny::plotOutput("base_mosaic") ) ), bslib::nav_spacer(), bslib::nav_item( bslib::input_dark_mode(id = "theme_toggle", mode = "light") ) ) server <- function(input, output, session) { # dark / light mode shiny::observeEvent(input$theme_toggle, { bslib::toggle_dark_mode(mode=input$theme_toggle) current_theme <- bslib::bs_current_theme() message("bg=", bslib::bs_get_variables(current_theme, "body-bg")[["body-bg"]]) par(bg = bslib::bs_get_variables(current_theme, "body-bg")[["body-bg"]]) }) output$base_mosaic <- shiny::renderPlot({ plot(1:10) }) } shiny::shinyApp(ui, server)
请问在bslib中确定当前背景与前景(文本)颜色的最佳方法是什么?
解决方案
核心问题
bs_current_theme()获取的是后端初始化的静态主题对象,而bslib的明暗模式切换是前端通过修改CSS变量实时生效的,并不会同步修改后端的主题对象,所以直接用它拿不到更新后的颜色值。此外,你之前的代码里把par()设置放在observeEvent中是无效的——每次renderPlot执行时,图形参数都会被重置,必须在renderPlot内部设置。
两种可靠实现方法
方法1:根据模式预定义颜色(适合默认主题)
利用input$theme_toggle返回的当前模式("light"或"dark"),提前定义对应模式的颜色值(匹配Bootstrap 5默认配色),直接在绘图时切换:
server <- function(input, output, session) { # 预定义Bootstrap 5默认明暗模式的颜色 theme_colors <- list( light = list(bg = "#ffffff", fg = "#212529"), dark = list(bg = "#212529", fg = "#ffffff") ) shiny::observeEvent(input$theme_toggle, { bslib::toggle_dark_mode(mode = input$theme_toggle) }) output$base_mosaic <- shiny::renderPlot({ current_mode <- input$theme_toggle # 在绘图前设置图形参数 par(bg = theme_colors[[current_mode]]$bg, fg = theme_colors[[current_mode]]$fg) plot(1:10) }) }
方法2:从前端CSS变量读取实时颜色(适合自定义主题)
如果使用了自定义主题,预定义颜色可能不匹配,这时候可以通过JS获取页面实时生效的CSS变量值,传给Shiny后端:
ui <- bslib::page_navbar( title = "Dark Mode Demo", theme = bslib::bs_theme(version=5), bslib::nav_panel( title = "Base Graphics", bslib::card( bslib::card_header("Mosaic"), shiny::plotOutput("base_mosaic") ) ), bslib::nav_spacer(), bslib::nav_item( bslib::input_dark_mode(id = "theme_toggle", mode = "light") ), # 添加自定义JS脚本,监听主题切换并传递颜色值 tags$script(HTML(' // 页面加载时传递初始颜色 document.addEventListener("DOMContentLoaded", () => { sendThemeColors(); }); // 监听主题切换事件,传递更新后的颜色 document.addEventListener("shiny:value", (event) => { if (event.name === "theme_toggle") { sendThemeColors(); } }); // 封装获取并传递颜色的函数 function sendThemeColors() { const root = getComputedStyle(document.documentElement); const bg = root.getPropertyValue("--bs-body-bg").trim(); const fg = root.getPropertyValue("--bs-body-color").trim(); Shiny.setInputValue("live_theme_colors", {bg, fg}); } ')) ) server <- function(input, output, session) { shiny::observeEvent(input$theme_toggle, { bslib::toggle_dark_mode(mode = input$theme_toggle) }) output$base_mosaic <- shiny::renderPlot({ # 等待颜色值传递过来 req(input$live_theme_colors) # 设置图形参数 par(bg = input$live_theme_colors$bg, fg = input$live_theme_colors$fg) plot(1:10) }) } shiny::shinyApp(ui, server)
总结
- 若使用默认Bootstrap主题,方法1简单高效,无需额外JS代码
- 若使用自定义主题或动态修改主题变量,方法2能准确获取实时生效的颜色
- 必须在
renderPlot内部设置par()参数,确保每次绘图都使用最新的颜色配置
内容的提问来源于stack exchange,提问作者Karsten W.
相关产品推荐
相关产品推荐

