--- title: "Introduction to animejs" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Introduction to animejs} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ## Overview `animejs` provides R bindings to [Anime.js v4](https://animejs.com), a JavaScript animation library. It produces self-contained HTML widgets via the `htmlwidgets` package, which render in browser environments like RStudio Viewer, R Markdown documents, Quarto reports, and Shiny applications. The package has three conceptual layers: 1. **Animations** -- `anime_animate()` describes a single animation; `anime_timeline()` and `anime_add()` accumulate a multi-segment animation specification in R. 2. **Properties** -- `anime_from_to()` and `anime_keyframes()` describe how individual CSS/SVG properties move over time. 3. **Rendering** -- `anime_render()` serialises the specification and wraps it in an htmlwidget. ## A minimal example Any SVG whose elements can be identified by a valid CSS selector can be targeted directly. ```{r minimal} library(animejs) svg_src <- ' ' anime_timeline( duration = 800, ease = anime_easing_elastic(), loop = TRUE ) |> anime_add( selector = anime_target_css("circle"), props = list( translateY = anime_from_to(-80, 0), opacity = anime_from_to(0, 1) ), stagger = anime_stagger(150, from = "first") ) |> anime_add( selector = anime_target_id("c2"), props = list(r = anime_from_to(20, 40)), offset = "+=200" ) |> anime_playback(controls = TRUE) |> anime_render(svg = svg_src, width = "360px") ``` ## Single animations `anime_animate()` is the R equivalent of Anime.js v4's `animate(targets, parameters)`: one set of property animations applied to one selector, without a timeline. ```{r animate} anime_animate( selector = anime_target_css("circle"), props = list( translateY = anime_from_to(-60, 0), opacity = anime_from_to(0, 1) ), ease = anime_easing_spring(), stagger = anime_stagger(120) ) |> anime_render(svg = svg_src, width = "360px") ``` Single animations support the same playback modifiers, event callbacks, and rendering options as timelines. ## Timelines `anime_timeline()` initialises an animation timeline. The `duration`, `ease`, and `delay` arguments set defaults that apply to every segment; individual segments may override them via `anime_add()`. ```{r timeline} tl <- anime_timeline( duration = 1000, ease = anime_easing("Cubic", "inOut"), loop = TRUE ) ``` `anime_add()` appends one segment -- a set of property animations applied to a CSS selector. The `offset` argument positions the segment on the timeline: `"+=0"` (default) starts immediately after the previous segment; `"+=200"` inserts a 200 ms gap. ## Property animations ### From/to `anime_from_to()` is the simplest property descriptor: a start value and an end value, with an optional CSS unit and per-property easing override. ```{r from_to, eval = FALSE} anime_from_to(0, 1) # opacity: 0 → 1 anime_from_to(0, 100, "px") # translateX: "0px" → "100px" anime_from_to(0, 1, ease = anime_easing_spring()) # per-property easing ``` ### Keyframes `anime_keyframes()` accepts two or more positional values. Bare values are used as `to` values; lists may additionally specify `ease`, `duration`, and `delay` per keyframe. ```{r keyframes, eval = FALSE} # Bare numeric keyframes anime_keyframes(0, 1, 0.5) # List-based keyframes with per-keyframe easing anime_keyframes( list(to = 0), list(to = 1, ease = "outQuad", duration = 400), list(to = 0.5, ease = anime_easing_bezier(0.4, 0, 0.2, 1)) ) ``` ## Staggering `anime_stagger()` distributes animation start times across the elements matched by the selector. It is passed to the `stagger` argument of `anime_add()` or `anime_animate()`. ```{r stagger, eval = FALSE} anime_add( tl, selector = ".bar", props = list(scaleY = anime_from_to(0, 1)), stagger = anime_stagger(80, from = "center") ) ``` The `start` argument adds a base delay before the staggered delays, and `reversed = TRUE` reverses the stagger order. For two-dimensional grid layouts, supply `grid = c(rows, cols)` and optionally `axis = "x"` or `axis = "y"`. ## Easing All easing constructors return `anime_easing` objects that map onto their Anime.js v4 equivalents: | Constructor | Anime.js v4 equivalent | |---------------------------------------|-----------------------------------| | `anime_easing("Quad", "out")` | `"outQuad"` | | `anime_easing_elastic()` | `"outElastic(1,0.3)"` | | `anime_easing_back()` | `"outBack(1.70158)"` | | `anime_easing_spring()` | `spring({bounce: .5, duration: 628})` | | `anime_easing_bezier(0.4, 0, 0.2, 1)` | `cubicBezier(0.4, 0, 0.2, 1)` | | `anime_easing_steps(10)` | `steps(10)` | Plain Anime.js v4 easing name strings (e.g. `"inOutSine"`, `"outElastic(1,0.3)"`) are also accepted wherever an `anime_easing` object is expected. Note that Anime.js v4 has removed the string syntax for cubic bezier, steps, and spring easings -- the R constructors handle this automatically by reconstructing the corresponding function calls in JavaScript. ## Playback `anime_playback()` sets looping, direction, speed, and UI controls on a timeline or single animation. Arguments left as `NULL` keep the settings already stored on the object. ```{r playback, eval = FALSE} tl |> anime_playback( loop = TRUE, loop_delay = 250, alternate = TRUE, playback_rate = 1.5, controls = TRUE ) |> anime_render(svg = svg_src) ``` Setting `controls = TRUE` injects a play/pause button and a scrub slider into the widget. ## Event callbacks `anime_on()` registers a global JavaScript function as a lifecycle callback. Inject the function into the page via `htmltools::tags$script()`. ```{r events, eval = FALSE} tl |> anime_on("onComplete", "myOnCompleteHandler") |> anime_render(svg = svg_src) ``` Valid events are `"onBegin"`, `"onBeforeUpdate"`, `"onUpdate"`, `"onRender"`, `"onLoop"`, `"onPause"`, and `"onComplete"`, matching the Anime.js v4 callback API. ## Shiny `animejsOutput()` and `renderAnimejs()` embed widgets in Shiny applications: ```{r shiny, eval = FALSE} library(shiny) ui <- fluidPage( sliderInput("duration", "Duration (ms)", 200, 2000, 800), animejsOutput("anim", height = "200px") ) server <- function(input, output, session) { output$anim <- renderAnimejs({ anime_animate( selector = anime_target_css("circle"), props = list(opacity = anime_from_to(0, 1)), duration = input$duration ) |> anime_render(svg = svg_src) }) } shinyApp(ui, server) ``` ## Saving widget output For reproducible documents the widget can be saved to a self-contained HTML file: ```{r save, eval = FALSE} widget <- anime_render(tl, svg = svg_src) htmlwidgets::saveWidget(widget, "animation.html", selfcontained = TRUE) ```