1

I have a shiny app with a title, one plot, some description and an actionButton. I want the elements to be aligned as follows:

  1. title on top;
  2. description and actionButton at the bottom;
  3. plot in between resizing according to the monitor resolution.

My app.R looks as follows:

library(shiny)

text <- list(
  "one",
  "one\ntwo",
  "one\ntwo\nthree",
  "one\ntwo\nthree\nfour"
)

ui <- fluidPage(
   titlePanel("title"),
   fluidRow(plotOutput("plot")),
   fluidRow(style = "position:absolute;bottom:20px;left:20px", 
            verbatimTextOutput("text"),
            actionButton("next_el", ">>")
   )
)

server <- function(input, output) {
   output$plot <- renderPlot({
      x    <- faithful[, 2] 
      bins <- seq(min(x), max(x), length.out = input$next_el + 1)
      hist(x, breaks = bins, col = 'darkgray', border = 'white')
   })
   output$text <- renderText(text[[input$next_el + 1]])
   observeEvent(input$next_el, {})
}

# Run the application 
shinyApp(ui = ui, server = server)

What I have at the moment: title, description and actionButton are placed ok on desktop and on mobile screens. description also resizes with the amount of text present.

However, the plot does not resize properly: On mobile devices the description is placed over the plot. On the other side on desktop devices the plot only uses about half the screen.

I tried resizing the plot with plotOutput("plot", height = "50%") and plotOutput("plot", height = "auto"), but then the plot disappears.

What can I do in order my plot resizes up if there's enough space and resizes down if there's not enough space?

symbolrush
  • 7,123
  • 1
  • 39
  • 67

1 Answers1

2

One way to approach this without additional css/html code is to set the height of the plot dynamically using the session$clientData object. Plot heights are set at a default value, but you can set the height to a dynamic value based on the plot width, which is responsive to the size of the browser window.

Updated server code would look like this:

server <- function(input, output, session) {
  output$plot <- renderPlot({
    x    <- faithful[, 2] 
    bins <- seq(min(x), max(x), length.out = input$next_el + 1)
    hist(x, breaks = bins, col = 'darkgray', border = 'white')
  }, height = function() {
    if (session$clientData$output_plot_width <= 1000) {
      (session$clientData$output_plot_width)*(3/4)
    } else { (session$clientData$output_plot_width)*(7/16) }
  })
  output$text <- renderText(text[[input$next_el + 1]])
  observeEvent(input$next_el, {})
}

where "plot" in output_plot_width matches the id of your plot.

You'll note that the code sets the plot height as a function of the width. You can change the aspect ratio by changing the fractions in the function. I also use two different aspect ratios, one for when the plot width is less than 1000 pixels (phones, tablets, etc.), and a different one when the user is more likely on a desktop or laptop screen.

To be fair, I haven't actually tested this on my phone - I just tested it using different window widths and aspect ratios in my browser.

phalteman
  • 3,442
  • 1
  • 29
  • 46
  • 1
    That basically did the trick. In order to also *scale down* the labels a little, I had to add another parameter `res = log(session$clientData$output_plot_width/30)*24` to the `renderPlot` function call. – symbolrush Mar 20 '19 at 14:55