Preamble
Let's get access to the D3.js library so that we can begin. In this case, we'll be including the library using the HTML <script>
tag.
<script src="https://d3js.org/d3.v7.js"></script>
Introduction
D3.js transitions support easing functions which can change the speed at which an animation progresses throughout its duration1. For example, we may wish to play an animation that moves quickly at the start, and then moves slowly towards the end:
This is different from the default behaviour which is cubic:
Let's modify an earlier example which aims to visualise a circle shape that starts with a radius of
<svg>
<circle cx="150" cy="75" r="50"></circle>
</svg>
Which will then gradually expand to a radius of
<svg>
<circle cx="150" cy="75" r="75"></circle>
</svg>
Except, this time we'll give it the appearance of bouncing back down to its original radius. These two transitions will occur one after another without any condition for termination, i.e. forever!
A Container for the Output
This is where you will see the output of the code cells that follow it, provided they are referencing the corresponding id
.
<div id="container"></div>
Creating an Empty SVG
We'll create a new detached <svg>
element and use the returned selection throughout the rest of this section.
svg = d3.create("svg");
Creating a Circle Element
Let's create our circle! We'll append the <circle>
element to our selection of the <svg>
element, and we'll use our starting coordinates
var circle = svg
.append("circle")
.attr("cx", 150)
.attr("cy", 75)
.attr("r", 50);
Animating the Circle Element with Easing
Let's create two functions, one to expand our circle, and another to shrink it. This will be similar to previous sections where we've expanded a circle with something much like the following:
function expandCircle() {
circle
.transition()
.duration(1000)
.attr('r', 75)
.on('end', contractCircle);
}
Except, this time we will introduce a minor modification to our expandCircle()
and contractCircle()
functions, where we apply an easing function to control the rate of change. In this case, let's use the d3.easePoly
and d3.easeBounce
easing functions within expandCircle()
and contractCircle()
, respectively.
There are many different easing functions available in D3.js. Examples, descriptions, and visualisations of each one can be found in the API reference2.
Function to Expand the Circle
We'll start with the function to expand the circle, which we'll name expandCircle()
.
function expandCircle() {
circle
.transition()
.ease(d3.easePoly)
.duration(1000)
.attr('r', 75)
.on('end', contractCircle);
}
When this transition ends, it will call the contractCircle()
function which doesn't exist just yet.
Function to Contract the Circle
Let's create our missing function to contract the circle, which we'll name contractCircle()
.
function contractCircle() {
circle
.transition()
.ease(d3.easeBounce)
.duration(1000)
.attr('r', 50)
.on('end', expandCircle);
}
This time, we can see we've added a listener function to call expandCircle()
when the transition ends.
Starting the Animation
The code that handles our transitions now appears within two functions. That means that nothing will happen on page load unless we invoke one of these functions directly to kick everything off. Let's start our looping transitions by invoking expandCircle()
, which will call contractCircle()
when it ends, and so on!
expandCircle();
Appending to the Container
Finally, let's append everything to our container.
d3
.select("#container")
.append(() => svg.node());
We can see the output by checking on our container with the corresponding id, which in this case is where id=container
.
Conclusion
If we inspect the HTML, we will see the <svg>
and <circle>
elements have been added to the <div>
where the id=container
. We can also see that the <circle>
element's r
attribute is gradually increasing to
<div id="container">
<svg>
<circle cx="150" cy="75" r="75"></circle>
</svg>
</div>