Visualisation with D3.js

A practical book on visualisation with D3.js that shows you how to create visualisations from the ground up that are engaging and beautiful.

Get the book
Looping Animated Transitions

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 control flow for advanced behaviour. For example, we may wish to play one transition after another and repeat them indefinitely to create the appearance of a looping animation.

As an example, we'll aim to visualise a circle shape that starts with a radius of 50.

<div class="d3js">
    <svg><circle cx="150" cy="75" r="50"></circle></svg>
</div>

Which will then gradually expand to a radius of 75.

<div class="d3js">
    <svg><circle cx="150" cy="75" r="75"></circle></svg>
</div>

Before shrinking 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 150,75 and radius of 50.

var circle = svg
    .append("circle")
    .attr("cx", 150)
    .attr("cy", 75)
    .attr("r", 50);

Animating the Circle Element on a Loop

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:

circle
    .transition()
    .duration(2000)
    .attr('r', 75);

Except, this time we will wrap the code in a function so that it can be called multiple times, and also introduce a listener that we will use for control flow.

Function to Expand the Circle

We'll start with the function to expand the circle, which we'll name expandCircle().

function expandCircle() {
    circle
        .transition()
        .duration(1000)
        .attr('r', 75)
        .on('end', contractCircle);
}

Besides wrapping the code in a function, we've introduced this new invocation of the transition.on(typenames, listener) function. This can add a listener function to the selection, which is invoked based on the event type. We've used an event type of end because we want our transition to end before calling the next one, but the following are also available1:

  • start - when the transition starts.
  • end - when the transition ends.
  • interrupt - when the transition is interrupted.
  • cancel - when the transition is cancelled.

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()
        .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 75 and then back down to 50 repeatidly, giving the appearance of an expanding and contracting circle.

<div id="container">
  <svg>
    <circle cx="150" cy="75" r="75"></circle>
  </svg>
</div>

  1. M. Bostock. d3-transition: Control Flow https://github.com/d3/d3-transition#transition_on. 

Comments

From the collection

Visualisation with D3.js

A practical book on visualisation with D3.js that shows you how to create visualisations from the ground up that are engaging and beautiful.

Get the book

ISBN

978-1-915907-05-9

Cite

Rostami, S. (2022). Visualisation with D3.js. Polyra Publishing.