gganime takes a ggplot2 plot written with gganimate syntax and renders it as an
animated SVG. You build the plot the same way you would for gganimate,
then call anime() in place of animate(). The
output is a self-contained HTML widget: one vector drawing of the scene
with a timeline over its elements, played in the browser.
gganime reuses gganimate’s build machinery unchanged and only replaces the final rendering step, so the gganimate documentation remains the reference for the grammar itself.
The animations below are live. Drag the scrub bar, or use the play and pause control, to step through a frame at a time.
Start from a static plot.
Add a transition to animate it. transition_states()
splits the data by a discrete variable and moves the points between the
resulting states.
To see the animation with gganimate you would print the object or
call animate(). With gganime you call
anime():
gganime supports three of gganimate’s transitions.
transition_states(), above, animates between the levels
of a discrete variable.
transition_time() animates along a continuous variable,
holding it to the data range so the spacing between frames matches the
spacing between values.
aq <- ggplot(airquality, aes(Day, Temp)) +
geom_point() +
transition_time(Month)
anime(aq, nframes = 30)transition_reveal() keeps earlier data on screen and
reveals the rest along a dimension, which suits a line growing over
time.
A transition exposes a set of per-frame variables. Insert them into the title, subtitle, or caption with glue syntax, and the text is swapped frame by frame in step with the geometry.
aq_labelled <- ggplot(airquality, aes(Day, Temp)) +
geom_point() +
transition_time(Month) +
labs(title = "Month: {frame_time}")
anime(aq_labelled, nframes = 30)Each transition provides different variables.
transition_states() gives closest_state;
transition_time() gives frame_time;
transition_reveal() gives frame_along. All
three also provide frame, nframes, and
progress.
A label may span several lines. Each line is swapped on its own, so a static first line can sit above a changing second one:
aq_two_line <- ggplot(airquality, aes(Day, Temp)) +
geom_point() +
transition_time(Month) +
labs(title = "Temperature by day\nMonth: {frame_time}")
anime(aq_two_line, nframes = 30)The number of lines has to be the same in every frame. The text layout is taken from the first frame, so a label that gains or loses a line partway through is held at its first-frame text with a warning.
ease_aes() sets how an aesthetic moves between values
across a transition. The default is linear; naming an aesthetic gives it
its own easing.
enter_*() and exit_*() control how data
that appears or leaves is drawn, so its entrance and exit can be
animated.
anim_species <- ggplot(iris, aes(Petal.Width, Petal.Length)) +
geom_point(aes(colour = Species), size = 2) +
transition_states(Species, transition_length = 2, state_length = 1)
anime(anim_species + enter_fade() + exit_shrink(), nframes = 30)Each family animates a different attribute: fade the paint opacity, grow and shrink the size, drift, fly and manual the position, recolour the paint. All of them work with the four supported geoms.
Two behaviours below come from gganimate itself and appear in its
gifs as well. enter_recolour() and
exit_recolour() set colour and
fill together, and setting the aesthetic a geom does not
paint makes tweenr coerce a logical column, which warns; pass
NA for that aesthetic, as in
enter_recolour(colour = "white", fill = NA) for the default
point shape. enter_grow() and exit_shrink() do
not change the geometry of a geom_ribbon() or
geom_area() layer, so a ribbon that arrives is drawn at its
full shape.
Layers are animated independently, so you can stack them the way you would in a static plot. Each layer keeps its own elements even when two layers draw the same kind of shape.
aq_layers <- ggplot(
airquality[airquality$Month %in% c(5, 6, 7), ],
aes(Day, Temp)
) +
geom_area(fill = "#9ecae1") +
geom_line(linewidth = 1, colour = "#08519c") +
geom_point(size = 2, colour = "#a50f15") +
labs(title = "Month {closest_state}") +
transition_states(Month, transition_length = 2, state_length = 1)
anime(aq_layers, nframes = 30)A layer that does not use the transition variable is drawn once and left in place, which is how you add a fixed reference against the animated data.
shadow_mark() keeps the marks from earlier frames on
screen behind the current one, so a scatter builds up as it
advances.
aq_shadow <- ggplot(airquality, aes(Day, Temp)) +
geom_point() +
transition_time(Month) +
shadow_mark(colour = "grey70") +
labs(title = "Month: {frame_time}")
anime(aq_shadow, nframes = 30)shadow_trail() keeps every nth frame instead, spacing
the marks out in time rather than showing all of them.
distance sets the spacing as a fraction of the animation,
and max_frames caps how many marks are kept, so the oldest
one drops away as a new one is added.
gganime fixes the frame count and playback speed when you call
anime(), the same way gganimate does at
animate():
nframes sets how many frames the animation is sampled
at (default 100).fps sets the playback speed in frames per second
(default 10), or give duration in seconds to
set the speed from the total length.loop repeats the animation: TRUE,
FALSE, or a number of iterations.controls shows or hides the play and scrub bar.width and height fix the widget size in
pixels; leaving them NULL lets it fill its container.precision rounds the animated coordinates, which trims
the file size.anime() returns an htmlwidget, so it prints in the
RStudio Viewer, knits into R Markdown and Quarto documents, and saves to
a standalone HTML file with htmlwidgets::saveWidget():
Loading gganime also makes a bare gganimate plot print through
anime() at the console, so printing anim gives
the widget without an explicit call. Set
options(gganime.autoprint = FALSE) to keep gganimate’s own
output.
In Shiny, pair renderGganime() in the server with
gganimeOutput() in the UI.
transition_states(),
transition_time(), transition_reveal().facet_wrap() and facet_grid(),
with fixed or free scales.coord_cartesian(), coord_fixed() /
coord_equal(), and coord_flip().enter_*() and exit_*(),
ease_aes(), and per-frame labels in the title, subtitle,
and caption.shadow_mark() and shadow_trail().Anything else stops with a message naming an alternative. Current
limits include non-linear coordinate systems
(coord_polar(), coord_radial(),
coord_transform(), coord_sf()),
shadow_wake(), and view_*() other than
view_static().