---
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 <- '
<svg viewBox="0 0 400 200" xmlns="http://www.w3.org/2000/svg">
  <circle data-animejs-id="c1" cx="50"  cy="100" r="20" fill="#4e79a7"/>
  <circle data-animejs-id="c2" cx="120" cy="100" r="20" fill="#f28e2b"/>
  <circle data-animejs-id="c3" cx="190" cy="100" r="20" fill="#e15759"/>
</svg>
'

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)
```
