back · main · about · writing · notes · reading · d3 · now · contact · uncopyright


Simple examples with transition chaining
2 Jun 2016 · 520 words

There are a few explanations and tutorials on the web about how to perform chained transitions on the same element using d3. Also existing are lots of demonstration: see this and this, for example.

I couldn’t find any dead-simple tutorial out there, however. This example will be just about as simple as it gets.

Let’s create a circle attached to a SVG element and use this as a running example. You can just do this in the console for a browser with d3 activated.

var svg  = d3.select("body").append("svg")
			.attr("height", 500)
			.attr("width", 800);

svg.append("circle")
	.attr("cx", 100)
	.attr("cy",100)
	.attr("r", 10)
	.style("fill", "black");

Basic chaining

Transitions can be queued for an element with transition.transition. We can line up transitions that start when the previous one ends.

d3.select("circle")
	.transition()
	.attr("cx", 200)
	.transition()
	.attr("cx", 40)
	.transition()
	.attr("cx", 400)
	.transition()
	.attr("cy", 200)
	.transition()
	.attr("r", 30)

Here we have a series of transitions acting on the circle. First we change its x position a few times, then its y position, then we increase its radius. All these transitions occur sequentially.

Chaining with delay

When we add delay into our transitions, getting the timing right needs a little bit of thought.

Imagine that a stopwatch is started as soon as the first transition starts (including any delay scheduled for before the start of the transition). You need to schedule your later transitions to take place only after the first transition is finished. Think of the number on the stopwatch after the first transition finishes, and adjust the timing so that later transitions only run after that number.

If you schedule two transitions acting on an element to take place at the same time, only the last one will run. As an example, the following won’t chain successfully:

//only the last transition runs because they have the same delay
d3.select("circle"
	.transition()
	.delay(500)
	.attr("cx", 200)
	.transition()
	.delay(500)
	.attr("cy", 200)

whereas this will

//delay is cumulative – the timer starts from when the first transition takes place 
d3.select("circle")
	.transition()
	.delay(500)
	.attr("cx", 200)
	.transition()
	.delay(1000)
	.attr("cy", 200)

You can also interrupt transitions using this method.

Keeping in mind that the default length of a transition is 250ms, you can see why the first transition is interrupted here:

// the first transition is interrupted when the second one takes place – so the circle won’t make it to 200
d3.select("circle")
	.transition()
	.delay(375)
	.attr("cx", 200)
	.transition()
	.delay(500)
	.attr("cy", 200)

We would have to delay the second transition by at least 625 milliseconds to avoid this interruption.

Chaining with delay and duration

In the spirit of the last example, when we specify both delay and duration in our transition we have to take them both into account.

Often some simple mental arithmetic is needed to work out by how much the chained transitions need to be delayed by.

//add together the delay and duration to figure out by how much to delay the second transition
d3.select("circle")
	.transition()
	.delay(500)
	.duration(500)
	.attr("cx", 200)
	.transition()
	.delay(1000)
	.attr("cy", 200)

Further Reading

Mike’s introduction to transitions should be considered mandatory reading for anyone beginning with transitions. Understanding selections is also vital to mastering transitions; this tutorial is excellent for this purpose.


back · main · about · writing · notes · reading · d3 · now · contact · uncopyright