Closed stla closed 1 year ago
I think that concatenating two animations doesn't work, because the second one will not wait for the first one to finish. One has to put the second one in the callback of the first one.
Here is a working way:
#' Define an animation
#'
#' Creates an `animation` object for usage in \link{runAnimation}.
#'
#' @param effect Animation effect used name to be applied.
#' Check \link{.cssEffects} object for possible options.
#' @param delay Delay of animation start (in milliseconds).
#' @param duration Duration of animation (in milliseconds).
#'
#' @return A named list with class `animation`.
#' @export
animation <- function(effect, delay = 0, duration = 1000) {
out <- list(
"effect" = match.arg(effect, .cssEffects),
"delay" = delay,
"duration" = duration
)
class(out) <- "animation"
out
}
animationRule <- function(anim, callbackBody){
settings <- json_settings(
delay = anim$delay,
duration = anim$duration,
callback = I(glue::glue("function() {{{callbackBody}}}"))
)
htmlwidgets::JS(glue::glue(
"$(this).animateCSS('{anim$effect}', {settings});"
))
}
chainTwoAnimations <- function(anim, rule){
animationRule(anim, callbackBody = rule)
}
chainAnimations <- function(...){
anims <- list(...)
nanims <- length(anims)
lastanim <- anims[[nanims]]
init <- animationRule(lastanim, callbackBody = "")
if(nanims == 1L){
return(init)
}
Reduce(chainTwoAnimations, anims[-nanims], init, right = TRUE)
}
#' Helpful methods for custom callback setup
#'
#' Can be used as a `true` or `false` argument for custom method of \link{js_calls}.
#'
#' @name custom-callbacks
#' @param ... Animation object(s) created with \link{animation}; if multiple
#' animation objects are given then the animations will be chained.
#' @param ignoreInit Should the animation be skipped when application is in initial state?
#'
#' @examples
#' conditionalJS(
#' shiny::tags$button("Hello"),
#' "input.value > 0",
#' jsCalls$custom(true = runAnimation("tada"))
#' )
#'
#' @export
runAnimation <- function(..., ignoreInit = TRUE) {
check <- TRUE # TODO
ignore_init <- if (ignoreInit) "true" else "false"
chain <- chainAnimations(...)
rule <- htmlwidgets::JS(glue::glue(
"var $element = $(this);",
"if (!{ignore_init} || $element.data('data-call-initialized')) {{",
"{chain};",
"}}"
))
class(rule) <- c(class(rule), "animate_call")
return(rule)
}
Usage example:
ui <- fluidPage(
actionButton("value", "Click me"),
br(), br(),
conditionalJS(
tags$h1("Hello", style = "display: none;"),
"input.value > 0",
jsCalls$custom(
true = runAnimation(animation("tada"), animation("swing")),
false = JS("$(this).hide();")
)
)
)
A couple of suggestions:
JS
function, to avoid the library(htmlwidgets)
call.style = "display: none;"
as in my above example, otherwise the hidden element shortly appears at the start-up, in a flash.@stla thank you for filing this issue. I've fixes inheritance of animate_call
class in this branch.
The issue with running multiple animations seems to be not supported in chain (what I think we're doing here by merging in a way) but should be done with using animateCSS callback: https://github.com/craigmdennis/animateCSS#chain-multiple-animations
Regarding your last suggestion for solution I'll take a look at it soon and respond with my thought. But for now, thanks a lot for your help and contribution here :)
but should be done with using animateCSS callback
This is exactly what my code does.
Cheers.
@stla Ahh yes, I can see it now. I do really like your suggestion, could you please create a PR (or should I do it)? Thanks!
Closing as this was fixed in v0.3 (already on CRAN).
Hello,
I'm trying to use
mergeCalls
for one hour now. This does not work (throws an error):It seems to me that the class
animate_call
is missing. With the following code, there's no error, but the second animation does not show up: