I'm building a Shiny app that displays various pre-rendered .png and .svg images across multiple tabs, with some of the images being chosen through different types of input. To add some pizzazz, I'd like to add animations to the images that play whenever an image is displayed, either when the tab that it's on is selected or when it is chosen through an input.
I've tried using shinyjs::show/hide and shinyjqui::jqui_effect, but those functions seem to want to respond to some input, like a button press, rather than playing automatically and repeatedly.
I've managed to put together the code below that uses shinyanimate to achieve the desired effect. However, my real app has many more tabs and images, and this method of having every animation react to any changes in the tabs or inputs seems inefficient. Is there a better way of doing this?
(N.B. I'm only using the "bounceInLeft" effect here because it makes the example clear, but I'd like to be able to use other animation effects such as "fadeIn").
library(shiny)
library(shinyanimate)
# Define UI
ui <- fluidPage(
withAnim(),
tabsetPanel(id = "tabs",
# Tab 1 ----
tabPanel("Tab 1",
fluidRow(
column(3,
imageOutput("tab1_img1")
),
column(3,
imageOutput("tab1_img2")
)
)
),
# Tab 2 ----
tabPanel("Tab 2",
selectInput("img_opts",
label = "Select image",
choices = c("img2", "img1")
),
imageOutput("tab2_imgs")
)
)
)
# Define server logic
server <- function(input, output) {
# Tab 1 image 1
output$tab1_img1 <- renderImage({
list(src = file.path("images/img1.png"), width = "95%")
}, deleteFile = FALSE)
# Tab 1 image 1 animation
observeEvent(input$tabs,
startAnim(session = getDefaultReactiveDomain(), "tab1_img1", "bounceInLeft")
)
# Tab 1 image 2
output$tab1_img2 <- renderImage({
list(src = file.path("images/img2.png"), width = "95%")
}, deleteFile = FALSE)
# Tab 1 image 2 animation
observeEvent(input$tabs,
startAnim(session = getDefaultReactiveDomain(), "tab1_img2", "bounceInLeft")
)
# Tab 2 images
output$tab2_imgs <- renderImage({
list(src = file.path(paste0("images/", input$img_opts, ".png")), width = "25%")
}, deleteFile = FALSE)
# Tab 2 animation
observeEvent(c(input$tabs, input$img_opts),
startAnim(session = getDefaultReactiveDomain(), "tab2_imgs", "bounceInLeft")
)
}
# Run the application
shinyApp(ui = ui, server = server)

You can achieve the same result with only one observer:
EDIT: using
shinyjquiEDIT: better solution
Here is a solution using the JavaScript library jquery.animatecss and the CSS library animate.css, which is the library used by shinyanimate. The app below requires an internet connection to include these libraries (see
tags$head); it's better to download them (and then to put them in the www subfolder).Here is the list of available effects:
A demo of these effects is available here.
In addition to the
durationoption, the JavaScript functionanimateCSS(used injs) also accepts adelayoption, if you want to delay the animation.You can improve this solution by allowing to set the desired effect and its options in
session$sendCustomMessage:EDIT
The images are visible during a very small moment before the animation starts. It seems that this code prevents this issue: