This variation of a donut chart demonstrates how to add labels with lines. Clicking on the button changes the displayed data. Check Pie Chart with Labels and Missing Data to see how to handle transitions with missing/new data.
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
width: 960px; | |
height: 500px; | |
position: relative; | |
} | |
svg { | |
width: 100%; | |
height: 100%; | |
} | |
path.slice{ | |
stroke-width:2px; | |
} | |
polyline{ | |
opacity: .3; | |
stroke: black; | |
stroke-width: 2px; | |
fill: none; | |
} | |
</style> | |
<body> | |
<button class="randomize">randomize</button> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
var svg = d3.select("body") | |
.append("svg") | |
.append("g") | |
svg.append("g") | |
.attr("class", "slices"); | |
svg.append("g") | |
.attr("class", "labels"); | |
svg.append("g") | |
.attr("class", "lines"); | |
var width = 960, | |
height = 450, | |
radius = Math.min(width, height) / 2; | |
var pie = d3.layout.pie() | |
.sort(null) | |
.value(function(d) { | |
return d.value; | |
}); | |
var arc = d3.svg.arc() | |
.outerRadius(radius * 0.8) | |
.innerRadius(radius * 0.4); | |
var outerArc = d3.svg.arc() | |
.innerRadius(radius * 0.9) | |
.outerRadius(radius * 0.9); | |
svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); | |
var key = function(d){ return d.data.label; }; | |
var color = d3.scale.ordinal() | |
.domain(["Lorem ipsum", "dolor sit", "amet", "consectetur", "adipisicing", "elit", "sed", "do", "eiusmod", "tempor", "incididunt"]) | |
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]); | |
function randomData (){ | |
var labels = color.domain(); | |
return labels.map(function(label){ | |
return { label: label, value: Math.random() } | |
}); | |
} | |
change(randomData()); | |
d3.select(".randomize") | |
.on("click", function(){ | |
change(randomData()); | |
}); | |
function change(data) { | |
/* ------- PIE SLICES -------*/ | |
var slice = svg.select(".slices").selectAll("path.slice") | |
.data(pie(data), key); | |
slice.enter() | |
.insert("path") | |
.style("fill", function(d) { return color(d.data.label); }) | |
.attr("class", "slice"); | |
slice | |
.transition().duration(1000) | |
.attrTween("d", function(d) { | |
this._current = this._current || d; | |
var interpolate = d3.interpolate(this._current, d); | |
this._current = interpolate(0); | |
return function(t) { | |
return arc(interpolate(t)); | |
}; | |
}) | |
slice.exit() | |
.remove(); | |
/* ------- TEXT LABELS -------*/ | |
var text = svg.select(".labels").selectAll("text") | |
.data(pie(data), key); | |
text.enter() | |
.append("text") | |
.attr("dy", ".35em") | |
.text(function(d) { | |
return d.data.label; | |
}); | |
function midAngle(d){ | |
return d.startAngle + (d.endAngle - d.startAngle)/2; | |
} | |
text.transition().duration(1000) | |
.attrTween("transform", function(d) { | |
this._current = this._current || d; | |
var interpolate = d3.interpolate(this._current, d); | |
this._current = interpolate(0); | |
return function(t) { | |
var d2 = interpolate(t); | |
var pos = outerArc.centroid(d2); | |
pos[0] = radius * (midAngle(d2) < Math.PI ? 1 : -1); | |
return "translate("+ pos +")"; | |
}; | |
}) | |
.styleTween("text-anchor", function(d){ | |
this._current = this._current || d; | |
var interpolate = d3.interpolate(this._current, d); | |
this._current = interpolate(0); | |
return function(t) { | |
var d2 = interpolate(t); | |
return midAngle(d2) < Math.PI ? "start":"end"; | |
}; | |
}); | |
text.exit() | |
.remove(); | |
/* ------- SLICE TO TEXT POLYLINES -------*/ | |
var polyline = svg.select(".lines").selectAll("polyline") | |
.data(pie(data), key); | |
polyline.enter() | |
.append("polyline"); | |
polyline.transition().duration(1000) | |
.attrTween("points", function(d){ | |
this._current = this._current || d; | |
var interpolate = d3.interpolate(this._current, d); | |
this._current = interpolate(0); | |
return function(t) { | |
var d2 = interpolate(t); | |
var pos = outerArc.centroid(d2); | |
pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1); | |
return [arc.centroid(d2), outerArc.centroid(d2), pos]; | |
}; | |
}); | |
polyline.exit() | |
.remove(); | |
}; | |
</script> | |
</body> |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Show comment Hide comment
Bjarkemonsted
commented
Aug 25, 2014
Hello David, |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Show comment Hide comment
nunoooo
Dec 4, 2015
hello,
Amazing graph cheers for this! But how to prevent that the labels overlap? i tried several solutions from stack overflow but none of the seem to work properly.
Good work cheers
nunoooo
commented
Dec 4, 2015
hello, |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Show comment Hide comment
cristianfierro
commented
Feb 16, 2016
Any news about the overlapping issue? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Show comment Hide comment
lingfulu
commented
Feb 13, 2017
hello!Ask a question!How to deal with the problem of overlapping text? |
Hello David,
Excellent transitions! I managed to get external data, and refresh on click, but I have a problem making it work in "Chrome" and "Firefox" browsers, the view windows is only in the upper 1/3 left corner, not showing the full graph. I tried adding .attr to the d3.select, but that didnt work. Any suggestions would be appreciated :)