admin管理员组

文章数量:1123274

I'm creating a Shiny app that uses R-Plotly to plot a timeseries for a dataset that's missing data from 2020. To work around this, I'm splitting the value column up into three columns: value_pre, value_2020, and value_post, where each column has its own respective trace in the plot. The data processing is shown below:

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

# Sample dataset
df <- data.frame(
  date = c("2010-01-1", "2011-01-01", "2012-01-01", "2013-01-01", "2014-01-01", 
           "2015-01-01", "2016-01-01", "2017-01-01", "2018-01-01", "2019-01-01", 
           "2020-01-01", "2021-01-01", "2022-01-01"),
  value = c(15, 20, 30, 25, 35, 50, 75, 50, 100, 150, NA, 200, 175)
)

# Splitting data into pre & post columns for their own trace. 
# Value for 2020 is an average of 2019 and 2021 values
df_split <- df |>
  mutate(date = as.Date(date),
         value_pre = ifelse(date < "2020-01-01", value, NA),
         value_2020 = case_when(date == "2019-01-01" ~ value,
                                date == "2020-01-01" ~ (lag(value) + lead(value)) / 2,
                                date == "2021-01-01" ~ value,
                                .default = NA),
         value_post = ifelse(date > "2020-01-01", value, NA))

In the plot, value_pre and value_post are solid lines whereas value_2020 is a dashed line that connects 2019 and 2021 on the plot. See the code below:

ui <- page_navbar(
  theme = bs_theme(version = 5),
  title = "Dashboard",
  nav_panel(
    title = "Plot",
    layout_columns(
      card(
        card_header("Histogram"),
        plotlyOutput(outputId = "distPlot"),
        height = "700px"
      ),
      card()  # Empty card to show sizing issue
    )
  )
)

server <- function(input, output, session) {
  output$distPlot <- renderPlotly({
    df_split |>
      plot_ly(
        type = "scatter",
        mode = "lines+markers+text"
      ) |>
      # The first trace is for 2020, so that the pre/post traces are overlayed 
      # on top of the this trace's markers for 2019 and 2021
      add_trace(
        x = ~date,
        y = ~value_2020,
        hoverinfo = "none",  # To prevent any hover label
        # Some basic coloring/grouping to show the intended plot
        line = list(color = "black", dash = "dash"),
        marker = list(color = "black"),
        legendgroup = "group",
        showlegend = FALSE
      ) |>
      add_trace(
        x = ~date,
        y = ~value_pre,
        text = ~value_pre,
        textposition = "top center",
        hovertemplate = "%{text}",
        line = list(color = "black"),
        marker = list(color = "black"),
        legendgroup = "group",
        name = "line",
        showlegend = TRUE  # Only legend item that shows up
      ) |>
      add_trace(
        x = ~date,
        y = ~value_post,
        text = ~value_post,
        textposition = "top center",
        hovertemplate = "%{text}",
        line = list(color = "black"),
        marker = list(color = "black"),
        legendgroup = "group",
        name = "line",
        showlegend = FALSE
      ) |>
      layout(
        # Ideally have this mode since it's easier for users to click near the
        # line instead of clicking on the line itself
        hovermode = "x unified",
        yaxis = list(title = "")
      )
  })
}

shinyApp(ui, server)

I've designed this such that the first trace drawn is the dashed line for value_2020, so that the markers for this trace are under the markers of value_pre and value_post. This produces the intended result where the hover label appears for the value_pre trace for 2019, while there isn't any hover label for the value_2020 trace while hovering over 2020 (besides the closest label from the surrounding traces, which is fine):

However, if the screen width is small enough, then the hover label reappears for the value_2020 trace when hovering over 2020:

Since I'm building a Shiny app where some users access via mobile, this issue comes up frequently and degrades the user experience. This issue also occurs in RStudio's viewer panel.

I've tried to replace hoverinfo = "none" with hoverinfo = "skip", but that just keeps the hover label for the value_2020 trace active all the time. I've also tried targeting the reappearing hover labels in the DOM, but have been unsuccessful in finding them. The issue is largely resolve when changing hovermode from "x unified" to "x" or "closest", however, it's not ideal since my actual plots usually contain multiple lines and the aesthetics of "x unified" are much easier to read.

I'm unsure what would solve this issue, but I'm open to changing the underlying plot structure if there is a better way to do so. I also came across this in the docs if it proves useful.

本文标签: How to hide a reappearing hover label for a trace in RPlotly within ShinyStack Overflow