NOTE: tweenr was released some time ago but a theft of my computer while
writing the release post meant that I only just finished writing about it now.
I’m very happy to once again announce a package update, as a new major version
of tweenr is now on CRAN. This release, while significant in itself, is also
an important part of getting gganimate on CRAN, so if you care about
gganimate but have never heard of tweenr you should be happy nonetheless.
tweenr was my first sort-of popular package and filled a gap in the gganimate
version of yore, where smooth transitions were something you had to bring
yourself. It has lived almost unchanged since its initial release, but as I
began to develop the next iteration of gganimate it became clear that new
functionality was needed. Some of it ended up in the
transformr but a huge chunk has
been added to tweenr itself. A description of everything new follows below.
Something new, something old {new_old}
The main API of the previous version of tweenr comprised of the
tween_states(), tween_elements(), and tween_appear(). All of these needed
serious change in both capabilities and API to the extend that all prior code
would break, so instead I decided to keep the old functions unchanged and create
new ones for a brighter future.
tween_states ⇒ tween_state/keep_state
tween_states() was perhaps the most used of the old functions. It takes a list
of data.frames and a specification of how long transitions between them should
take and how long it should pause at each data.frame. This is perfect for
situations where you have discrete states and want to have a smooth transition
between them. Still it had certain shortcomings, such as requiring that each
data.frame included the same number of rows etc. (meaning that each “element”
should be present in each state). Further I have ended up finding it a bit
clumsy to use. The new function(s) that should serve the same needs as
tween_states() is tween_state() (and keep_state()) and they are much more
powerful. The biggest difference, perhaps, is that tween_state() only takes a
from and to state and not an arbitrarily long list of states. If transitions
are needed between more states you’ll need to chain calls together (potentially
with keep_state() if the state should pause between transitions). It will
something like this:
library(tweenr)
irises % keep_state(5) %__% tween_state(irises$virginica, ease = 'linear', nframes = 15) %__% keep_state(5)
As can be seen, if you like piping you’ll feel right at home. Apart from the
seemingly superficial change in API, the tween_state() also packs some new
tricks. One of these is per-column easing, so you can specify different easing
functions for different variables. Another, more fundamental one, is the
possibility of specifying an id to match rows by. Ultimately this means that
rows no longer needs to be matched by position and that you can now tween
between states with different numbers of elements. All of this is so important
that it will get its own section later on in Enter and Exit.
tween_elements ⇒ tween_components
While tween_states() was probably the most used of the legacy functions it was
by no means the only one. tween_elements() was a very powerful function that
let you specify different individual states for each element in a single
data.frame and then expand this to an arbitrary number of frames. The changes
that its hier bring is less dramatic than what happened with tween_states(),
and simply adds the same features that tween_state() introduced. This means
per-column easing and the same features as described below in
Enter and Exit. Further it changes the semantics of a couple of
variables so they are now tidy evaluated. This means that state specifications
can be calculated on the fly, rather than having to exist inside the data.frame
to be tweened.
tween_appear ⇒ tween_events
tween_appear() never really felt right. The purpose was to let each row appear
in a specific frame while still allowing the user to define how it should
appear. To this end it expanded the data.frame by giving each row an age in
each frame (negative age meant that it had yet to appear) and then let the user
do with this as they pleased. What was missing was the whole idea of
Enter and Exit which I have already plugged multiple times.
tween_event() is a pretty radical change in order to solve the problem that
tween_appear() originally tried (but failed) to solve.
Enter and Exit
Before we go any further with other new tweening functions I think I owe it to
you to describe what all this entering and exiting I’ve been talking about
really is. If you have dabbled in D3.js the two words will be familiar but they
have slightly different meaning in tweenr. In D3 enter and exit describe a
selection of data that did not match in a data join between current and next
state, while in tweenr it is a function that modifies data that is going to
appear or disappear. If enter and/or exit is not given, the data will just pop
into existence in the first frame it relates to and disappear without a trace
after the last frame it relates to has ended. if you provide e.g. an enter
function this will be applied to all elements when they first appear. The result
of the function will then be inserted into the tweening prior to the original
data so that any changes the function does will gradually change to the original
data. This may sound quite confusing, but in essence it means that if you pass
in an enter function that sets the transparancy variable to zero you’ll get a
gradual fade-in effect. The exit function is just like it, but in reverse. But
let’s see it in effect instead:
df1