Contents

How to Use Transitions in D3.js?

Contents
Hello! In a previous post we have seen how to make your bar chart dynamic. Today, I will show you how to use transitions in your visualizations to smooth out your updates.

Basically, to add transitions to our code, we just need to use d3.transition() just before setting the attributes of our shapes.

For instance, if we take the bar chart that we used in previous posts, it should looks like this:

1
2
3
4
5
6
7
8
rectangles.enter().append("rect")
	.attr("y", d => y(0))
	.attr("x", (d) => x(d.year))
	.attr("width", x.bandwidth)
	.attr("height", d => 340 - y(d[value]))
	.attr("fill", "green")
	.transition(d3.transition().duration(500))
		.attr("x", (d) => y(d[value]))

So here y will begins with the value 0 and ends at d[value] (“revenue” or “profits” regarding to the flag value, see: https://www.datackling.com/post/how-to-make-your-chart-dynamic-in-d3-js ). All attributes defined after the transition will be applied gradually using the duration in parenthesis (here 500 milliseconds).

We can do it in a better way, defining the transition as a variable:

const t = d3.transition().duration(500);

It is better because we don’t have to code the transition repeatedly and we can change the duration in just one place.

Now, let’s add a transition to our bar chart. First we add the variable t:

1
2
3
4
5
6
7
8
9
function update(data){
   const value = flag ? "profit" : "revenue"
   
   const t = d3.transition().duration(500)

   y .domain([0, d3.max(data, d => d[value])])    // input
   x.domain(data.map(d => d.year))    // input
   ...   
}

It’s important to set a duration time lower than the value of the loops delay.

Concerning the axis, all we need to do is to place a transition before the call method.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
const xAxisCall = d3.axisBottom(x)
  g.append("g")
       .attr("class", "x axis")
       .attr("transform", `translate(0, 340)`)
       transition(t).call(xAxisCall)
       .selectAll("text")
       .attr("y", "10")
       .attr("x", "-5")
       .attr("text-anchor", "end")
       .attr("transform", "rotate(-40)")
  const yAxisCall = d3.axisLeft(y)
   .ticks(3)
   .tickFormat(d => "$"+d)
  g.append("g")
     .attr("class", "y axis")
     transition(t).call(yAxisCall)

We now have a smooth transition on the axis.

We can add a transition to the exit() method too. Let’s do it:

1
2
3
4
5
6
rectangles.exit()
	.attr("fill", "red")
	.transition(t)
		.attr("height", 0)
		.attr("y", y(0))
		.remove()

For updating bars, we will just add a transition before setting the attributes.

1
2
3
4
5
rectangles.transition(t)
	.attr("y", d=> y(d[value]))
	.attr("x", d => x.(d.year))
	.attr("width", x.bandwith)
	.attr("height", d => 340 - y(d[value]))

Finally, we are going to make these new bars come in from the ground up like we saw at the beginning of this post:

1
2
3
4
5
6
7
8
9
rectangles.enter().append("rect")
	.attr("y", d => y(0))
	.attr("x", (d) => x(d.year))
	.attr("width", x.bandwidth)
	.attr("height", 0)
	.attr("fill", "green")
	.transition(d3.transition().duration(500))
		.attr("y", (d) => y(d[value]))
		.attr("height", d => 340 - y(d[value]))

You can see that the code here is not particularly succinct. Something that we can do is to use the merge method to set the attributes of the enter and update selections at the same time.

Let me show you:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
rectangles.enter().append("rect")
	.attr("fill", "green")
	.attr("y", y(0))
	.attr("height", 0)
	.merge(rects)
	.transition(t)
		.attr("x", d => x(d.year))
		.attr("width", x.bandwith)
		.attr("y", d => y(d[value]))
		.attr("height", d => 340 - y(d[value]))

We call the merge method on our enter selection. All the code before the merge selection will be applied to the enter selection and the code after the merge selection will be applied to the enter and update selection.

Now you have a bar chart that switch smoothly between two datasets (profit and revenue).

I hope you enjoyed this post, feel free to ask your questions in DM. See you!