Tom MacWright

tom@macwright.com

Animating d3 and React

This is part 2 to Mixing d3 and React, filling in the blank of “how to do animation”

Tiger

In the last episode, I talked about the ideal way of combining d3 and React, by using d3’s excellent visualization methods but only using React’s way of building documents. d3 has its own way of building documents, but using it within a React environment leads to unnecessary complexity.

I laid out the technique for non-animated documents, but I didn’t mention animation. Since animation is important to many people and this approach works well with animation, let’s connect the dots.

Cheng Lou’s phenomenal react-motion project is the best building block for React projects that need animation. The gist of its API is

import {Motion, spring} from 'react-motion';
// In your render...
<Motion defaultStyle={{x: 0}} style={{x: spring(10)}}>
  {value => <div>{value.x}</div>}
</Motion>

Perhaps confusingly, the initial and destination states are called defaultStyle and style, but they don’t have to be styles: they can be any number used for any reason.

What react-motion gives you is a component that provides smoothly ‘animated’ or interpolated values. In the above example, what you see on your page is something like:

<div>0</div> <!-- initial render -->
<div>0.01</div> <!-- and then a few milliseconds later -->
<div>1</div> <!-- and then at the end of the animation -->

So react-motion repeatedly renders content with smoothly updating values.

Let’s apply that idea to d3, echoing Mike Bostock’s Pie Chart Update II example:

Here’s the change necessary to transition those slices:

Paths without transition:

<path
  fill={color(i)}
  d={arc(value)} />

Paths with transition:

<Motion
  key={i}
  defaultStyle={{
    startAngle: slice.startAngle,
    endAngle: slice.endAngle,
    padAngle: slice.padAngle,
  }}
  style={{
    startAngle: spring(slice.startAngle),
    endAngle: spring(slice.endAngle),
    padAngle: spring(slice.padAngle)
  }}>
{value => <path
  fill={color(i)}
  d={arc(value)} />}
</Motion>

Like d3’s transitions, you can customize the way react-motion animates. Here’s that same example but with fast and springy animation:

To review, the process of animating this style of React & d3 code is to:

  1. Wrap the part of your visualization that you’d like to animate in a Motion component
  2. Determine the variables that will be part of the transition. In this case, it was the components of the slice variable - startAngle, endAngle, and padAngle. I found these by console.log‘ing the value, but you could also read the documentation.

Caveats

This works for a wide range of interpolations, not 100% of d3’s: react-motion interpolates numbers, whereas d3 can interpolate things as complex as SVG paths. Luckily, you can use d3’s interpolation logic too! But this time, that will really be left as an exercise for the reader.

Tiger graphic from the excellent Public Domain Review website