rshinyecharts4rbslib

How to make a radar chart fit any screen without labels cutting off?


I am creating a radar chart in my app. It looks okay on big screens but the labels cut off on small screens:

enter image description here

What changes can I make to the layout that shows all the labels correctly on any screen size?

Code Example

library(shiny)
library(bslib)
library(echarts4r)

ui <- page_navbar(
  sidebar = sidebar(),
  nav_panel(
    title = "Chart",
    echarts4rOutput("radar", height = "500px")
  )
)

server <- function(input, output, session) {
  output$radar <- renderEcharts4r({
    df <- data.frame(
      x = c("Openness", "Conscientiousness", "Extroversion", "Agreeableness", "Neuroticism"),
      y = runif(5, 1, 5),
      z = runif(5, 3, 7)
    )
    
    df |>
      e_charts(x) |>
      e_radar(y, max = 7) |>
      e_radar(z) |>
      e_tooltip(trigger = "item") |> 
      e_radar_opts(
        axisName = list(
          color = "black",
          fontFamily = "Libre Franklin",
          fontSize = 18#,
          # formatter = JS("function (value, indicator) {
          #             indicator.nameRotate = 45;
          #             return value;
          #           }")
        )
      )
  })
}

shinyApp(ui, server)

Solution

  • I am sure your actual code has a lot more going on then just this plot, so I've included several modifications that you can pick and choose to use or throw out, depending on what works best for you.

    Additionally, this is set up for Shiny. It won't work as is outside of Shiny.

    In your ui, you've named the plot renderer radar. This becomes a keyword in the background and is used in the JS code I've added here. If you change that name, you have to change it in the JS, as well.

    In the plot, I've added the options axisNameGap and center.

    I added a JS script added to the header via the ui. In this code, there are 2 functions for resizing: one to resize the plot labels' font size and one to resize the legend text. There is also a timeout function that initially resizes the plot so that the width to height ratio is 3/2.

    The timeout function includes one last function It creates an event that is triggered whenever the window is resized. This calls for font size and plot size adjustments, based on the width of the window. (Where the window is the browser window.)

    If you have any questions, please let me know.

    library(shiny)
    library(bslib)
    library(echarts4r)
    
    ui <- page_navbar(
      tags$head(
        tags$script(HTML(
          "radarFontSize = () => {    /* for plot labels */
            let width = document.getElementById('radar').offsetWidth;
            let nfs = Math.max(Math.round(width / 45), 6);                  /* never less than 6 */
            return nfs;
          }
          
          legFontSize = () => {    /* for legend labels */
            let width = document.getElementById('radar').offsetWidth; /* width determins font size*/
            let nfs = Math.max(Math.round(width / 50), 6);                  /* never less than 6 */
            return nfs
          }
    
          setTimeout(function() {
                 /* 'radar' here is from the name assigned in echarts4rOutput in the UI */
            e = echarts.getInstanceById(radar.getAttribute('_echarts_instance_'));
            w = document.getElementById('radar').offsetWidth * .9;
            e.resize({width: w, height: Math.round(w * 3/5)});             /* resize on render */
          
            window.onresize = () => {                                      /* resize on resize */
              w = document.getElementById('radar').offsetWidth * .9;
              e.resize({width: w, height: Math.round(w * .65)});           /* fit in your hole */
              e.setOption({radar: {axisName: {fontSize: radarFontSize()}}, /* adjust plot to window */
                           textStyle: {fontSize: legFontSize()} });
            }
          }, 300)") # give me a minute to load...
        )
      ),
      sidebar = sidebar(),
      nav_panel(
        title = "Chart",
        echarts4rOutput("radar")
      )
    )
    
    server <- function(input, output, session) {
      output$radar <- renderEcharts4r({
        df <- data.frame(
          x = c("Openness", "Conscientiousness", "Extroversion", "Agreeableness", "Neuroticism"),
          y = runif(5, 1, 5),
          z = runif(5, 3, 7)
        )
        
        df |>
          e_charts(x) |>
          e_radar(y, max = 7) |>
          e_radar(z) |>
          e_tooltip(trigger = "item") |> 
          e_radar_opts(
            center = c('52.5%', '60%'),   # <-------- I'm new!
            axisNameGap = 5,              # <-------- I'm new!
            axisName = list(
              color = "black",
              fontFamily = "Libre Franklin",
              fontSize = 18
            )
          )
      })
    }
    
    shinyApp(ui, server)
    

    scaled plot size

    scaled plot 2

    plot 3