Recently, I read a terrific article from Swizec Teller about using React and D3 to do a pretty cool letter-shuffle animation:
D3 is a data visualization library that uses SVGs to do stuff that would otherwise be pretty tricky with more-traditional DOM nodes. It’s a terrific library, but it’s also pretty large (both in terms of file-size and API-size). For something like this, it feels a bit like using a leaf-blower to put out a candle.
Today I’d like to share a method of achieving a similar effect using the plain ol’ DOM you k̶n̶o̶w̶ ̶a̶n̶d̶ ̶l̶o̶v̶e are already familiar with.
There are, of course, many very good reasons to use D3! This post is not meant to convince you that it isn’t worth using, just that for small animations, simpler methods exist :)
The End Result
Take a peek at what we’re building!
The Basic Skeleton
Here’s the minimum representation needed to create shuffling text, without any animations:
No magic here. We have a function that generates a random subset of the alphabet, and an interval that uses that function t0 update the state every 1500 milliseconds.
Making it Fluid with Flip Move
To start with, let’s ignore the enter/exit effects, and just focus on the letters that persist between shuffles.
In the original example, if a letter is lucky enough to survive, it slides gracefully to its new position.
This is possible to calculate, using some maths; essentially, we figure out where the letter is, where the letter is about to be, and work some magic using the delta as a CSS prop that can be transitioned.
Curious minds can read more about this process. Rest assured, though, we don’t need to worry about any of this today!
React Flip Move is a library I wrote to solve the problem of list re-orderings, but it has a few extra tricks up its sleeves. Let’s take a look at how it can help solve this problem:
Sweet! Let’s look at how this works…
First, we needed to wrap each letter in a <span>, and give it a key attribute. This is how FlipMove keeps track of items, so that when the letter E turns up in a different location in the next state, it knows it’s the same E and should transition smoothly to that new spot.
We also need to ensure that our elements have a `display` property that isn’t inline. Inline items are notoriously hard to position, and by setting them to inline-block, we guarantee that our items can be moved about easily.
That’s about it, for letter re-ordering! Flip Move does all the heavy lifting for us, by figuring out how to animate things when the props change.
Entering and Leaving
Our work is not yet done, however! In the original example, items have this neat enter/leave cycle where they come in from above and leave by descending below.
Flip Move 2.0 supports enter/leave animations. It offers a few presets, but we’ll need to do something custom to achieve this effect.
We want things to start at, say, -30px above where they are on the vertical axis, with 0 opacity. They ought to transition to be in their normal position, and fully opaque.
For the exit, the opposite is true; we start fully opaque at 0px. The transition should take us to +30px on the vertical axis, with 0 opacity.
Here’s how this works:
A Splash of Colour
We’re getting pretty close! There’s one thing missing though: colour.
In the original example, new items are coloured green. They remain that colour until the animation begins, at which point they either switch to black, if they’ve survived the shuffle, or to red, if they’re being removed.
To handle this, we’ll need to make use of FlipMove’s callbacks.
FlipMove has hooks at the start of the animation, and they’re called with the ReactElement representing the instance, and the backing instance DOM node itself.
Internally, FlipMove adds properties to the ReactElement to keep track of their state. Brand new items have entering: true, and items on the way out have leaving: true.
We can use those props to selectively apply classes directly to the DOM nodes:
Why so imperative?
You may have realized that by setting a class directly on the node, we’re breaking out of React’s declarative abstraction.
The truth is, it’s hard to do stuff like this declaratively. I worked out a proper solution, but it basically had to re-implement FlipMove to keep track of entering/leaving letters.
Sometimes, the simplest solution is the best one. React provides escape hatches for times when the abstraction gets in the way, and IMHO this is a perfect use-case.
I attached a stylesheet to fill in the class names for `enter` and `leave`, and made a few other small tweaks to match the original demo: