如何设置一个独立的进度条

How to set up an independent progress bar

我试图在我闪亮的应用程序的计算过程中包含一个进度条。我的问题描述:

目前有一些与问题相关但没有满意答案的问题: here, here 例如。

有没有一种方法可以实现在计算之上进行的柱状图,独立连续,固定数量时间(或者可能在模仿栏的弹出窗口中插入栏的动画?)

谢谢

编辑:我试图用动画模仿进度条sliderInput,但我找不到如何以编程方式触发动画...

不是一个完整的答案,因为我的建议是使用 progress bars,但我希望它能有所帮助。

这里有一种使用 shinyjs 包的 javascript 来触发点击滑块动画按钮的方法:

library(shiny)
library(shinyjs)

jscode <- "
  shinyjs.play = function() {
    $('.slider-animate-button').trigger('click');
  }
"

ui <- fluidPage(
  useShinyjs(),
  extendShinyjs(text = jscode),
  sliderInput("slider", label = "", width = '600px',
              min = 0,
              max = 20,
              value = 0,
              step = 1,
              animate = animationOptions(
                interval = 100,
                playButton = "Play",
                pauseButton = "Pause"
              )
  )
)

server <- function(input, output,session) {
  observe( {
    js$play()
  })
}

shinyApp(ui, server)

请注意,js 代码引用了 slider-animate-button class,因此它会触发应用中的每个滑块动画选项。

感谢@GyD 的 回答,我现在提出一个改进的解决方案(我承认它有一些 hack)。 此处通过所需持续时间的 sys.sleep 来模拟长时间计算。您会看到在 'sleep' 期间仍然有滑块移动。我将动画滑块放入 RenderUI 以便我们可以控制速度:

library(shiny); library(shinyjs); library(shinyWidgets)
jscode <- "
shinyjs.play = function() {
$('.slider-animate-button').trigger('click');
}
"
ui <- fluidPage(
     tags$head(tags$style(HTML('.irs-from, .irs-to, .irs-min, .irs-max, .irs-grid-text, .irs-grid-pol, .irs-slider {visibility:hidden !important;}'))),
     useShinyjs(), extendShinyjs(text = jscode),
     numericInput("seconds", "how many seconds your calculation will last?", value=6),
     uiOutput("UI"),
     actionButton("go", "Compute"))
server <- function(input, output,session) {
     disable("slider")
     observeEvent(input$go, priority=10, {
          js$play()
          Sys.sleep(input$seconds) # simulate computation
          showNotification("Computation finished!", type="error")})
     output$UI = renderUI({
          sliderInput("slider", label = "", width = '300px',min = 0,max = 100,value = 0,step = 1,
                      post="% done",
                      animate = animationOptions(
                           interval = (as.numeric(input$seconds)*8),
                           playButton = "",
                           pauseButton = ""))})}
shinyApp(ui, server)

滑块真的很像条形,不是吗?

我认为当 Shiny 发布其异步支持时,这会容易得多。但就目前而言,它必须是自定义的客户端 JavaScript 解决方案。

我对它的看法使用与 Shiny 相同的 Bootstrap 3 progress bars。出于懒惰,我还利用了 Shiny 的进度条 CSS 类(top bar 样式),因此这将与 Shiny 的进度条发生冲突。理想情况下,它应该是一个具有自己样式的小部件。

我使用 jQuery 的 animate 来设置固定持续时间内进度条的宽度。 animate 有一些开箱即用的好选项,比如缓动。我还默认让进度条在 100% 后徘徊,认为服务器显式关闭进度条会更好,以防时间不准确。

library(shiny)

progressBarTimer <- function(top = TRUE) {
  progressBar <- div(
    class = "progress progress-striped active",
    # disable Bootstrap's transitions so we can use jQuery.animate
    div(class = "progress-bar", style = "-webkit-transition: none !important;
              transition: none !important;")
  )

  containerClass <- "progress-timer-container"

  if (top) {
    progressBar <- div(class = "shiny-progress", progressBar)
    containerClass <- paste(containerClass, "shiny-progress-container")
  }

  tagList(
    tags$head(
      tags$script(HTML("
        $(function() {
          Shiny.addCustomMessageHandler('progress-timer-start', function(message) {
            var $progress = $('.progress-timer-container');
            var $bar = $progress.find('.progress-bar');
            $bar.css('width', '0%');
            $progress.show();
            $bar.animate({ width: '100%' }, {
              duration: message.duration,
              easing: message.easing,
              complete: function() {
                if (message.autoClose) $progress.fadeOut();
              }
            });
          });

          Shiny.addCustomMessageHandler('progress-timer-close', function(message) {
            var $progress = $('.progress-timer-container');
            $progress.fadeOut();
          });
        });
      "))
    ),

    div(class = containerClass, style = "display: none;", progressBar)
  )
}

startProgressTimer <- function(durationMsecs = 2000, easing = c("swing", "linear"),
                               autoClose = FALSE, session = getDefaultReactiveDomain()) {
  easing <- match.arg(easing)
  session$sendCustomMessage("progress-timer-start", list(
    duration = durationMsecs,
    easing = easing,
    autoClose = autoClose
  ))
}

closeProgressTimer <- function(session = getDefaultReactiveDomain()) {
  session$sendCustomMessage("progress-timer-close", list())
}

ui <- fluidPage(
  numericInput("seconds", "how many seconds your calculation will last?", value = 6),
  progressBarTimer(top = TRUE),
  actionButton("go", "Compute")
)

server <- function(input, output, session) {
  observeEvent(input$go, {
    startProgressTimer(input$seconds * 1000, easing = "swing")
    Sys.sleep(input$seconds) # simulate computation
    closeProgressTimer()
    showNotification("Computation finished!", type = "error")
  })
}

shinyApp(ui, server)