We learn about d3.transition, and talk about when we would use it instead of CSS transitions. We then update our histogram to animate using d3.transition.
CSS transitions have our back for simple property changes, but for more complex animations we'll need to use
d3.transition() from the d3-transition module. When would we want to use
d3.transition() instead of CSS transitions?
When we want to ensure that multiple animations line up
When we want to do something when the animation ends (for example starting another animation)
When the property we want to animate isn't a CSS property (remember when we tried to animate our bars' heights but had to use
d3.translatecan animate non-CSS property changes.)
When we want to synchronize adding and removing elements with animations
When we might interrupt halfway through a transition
When we want a custom animation (for example, we could write a custom interpolator for changing text that adds new letters one-by-one)
Let's get our hands dirty by re-implementing the CSS transitions for our histogram.
Let's again start by animating any changes to our bars. Instead of adding a
transition property to our
styles.css file, we'll start in the
chart.js file where we set our
As a reminder, when we run:
we're creating a d3 selection object that contains all
<rect> elements. Let's log that to the console as a refresher of what a selection object looks like.
We can use the
.transition() method on our d3 selection object to transform our selection object into a d3 transition object.
d3 transition objects look a lot like selection objects, with a
_groups list of relevant DOM elements and a
_parents list of ancestor elements. They have two additional keys:
_name, but that's not all that has changed.
Let's expand the
__proto__ of our transition object.
In this case, we can see that the
__proto__property contains d3-specific methods, and the nested
__proto__object contains native object methods, such as
We can see that some methods are inherited from d3 selection objects (eg.
.each()), but most are overwritten by new transition methods. When we click the Change metric button now, we can see that our bar changes are animated. This makes sense — any
.attr() updates chained after a
.transition() call will use transition's
.attr() method, which attempts to interpolate between old and new values.
Something looks strange though - our new bars are flying in from the top left corner.
Note that d3 transitions animate over 0.25 seconds — we'll learn how to change that in a minute!
<rect>s are drawn in the top left corner by default, this makes sense. But how do we prevent this?
Remember how we can isolate new data points with
.enter()? Let's find the line where we're adding new
<rect>s and set their initial values. We want them to start in the right horizontal location, but be 0 pixels tall so we can animate them "growing" from the x axis.
Let's also have them be green to start to make it clear which bars we're targeting. We'll need to set the fill using an inline style using
.style() instead of setting the attribute in order to override the CSS styles in
Why are we using
.attr()to set the fill? We need the
fillvalue to be an inline style instead of an SVG attribute in order to override the CSS styles in
styles.css. The way CSS specificity works means that inline styles override class selector styles, which override SVG attribute styles.