Smooth Pie Chart Transitions with D3.js

August 27, 2016
Category: TIL
Tags: D3.js and Javascript

A few days ago we made a pie chart that updates in real time, but it has one issue: The update is jumpy. Let’s fix that.

I’ll admit, unlike the other tutorials where I was able to figure out most of this on my own, I had to mine other examples to figure this out. I primarily followed Mike Bostock’s Pie Chart Update, II, but his commented Arc Tween example was extremely helpful in understanding what d3.interpolate is doing.

What to do

Note: We’re starting with code from my previous Pie Chart Update tutorial.

First we need to store the beginning angle for each arc. You’ll recall that I’m using two separate arcs, one for the main chart and one for the labels. For about 10 minutes I was trying to figure out why the first update was jumpy but all subsequent ones were smooth. It turns out that we need to store the initial angles for each set of arcs:

g.append("path")
	.attr("d", arc)
	.style("fill", function(d) { return color(d.data.letter);})
	.each(function(d) { this._current = d; }); // store the initial angles;

g.append("text")
	.attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; })
	.text(function(d) { return d.data.letter;})
	.style("fill", "#fff")
	.each(function(d) { this._current = d; }); // store the initial angles;

Next we need to write two arcTween functions to transition between the two. I followed Mike Bostock’s example for the first one, then adapted it to the label arc, too:

function arcTween(a) {
  var i = d3.interpolate(this._current, a);
  this._current = i(0);
  return function(t) {
    return arc(i(t));
  };
}

function labelarcTween(a) {
  var i = d3.interpolate(this._current, a);
  this._current = i(0);
  return function(t) {
    return "translate(" + labelArc.centroid(i(t)) + ")";
  };
}

Last we need to include these arcTween() functions into the change() function we wrote before. I commented out the previous updates so you can compare them. The duration is 1/2 a second:

function change() {
	var pie = d3.pie()
		.value(function(d) { return d.presses; })(data);
	path = d3.select("#pie").selectAll("path").data(pie);
	//path.attr("d", arc);
	path.transition().duration(500).attrTween("d", arcTween); // Smooth transition with arcTween
	//d3.selectAll("text").data(pie).attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; });
	d3.selectAll("text").data(pie).transition().duration(500).attrTween("transform", labelarcTween); // Smooth transition with labelarcTween
}

Here is is in action. As always, you can view source to see the fully integrated example:

Find this post useful?

Buy me a coffeeBuy me a coffee