Skip to content

Instantly share code, notes, and snippets.

@COLTstreet
Forked from maybelinot/.block
Last active November 19, 2019 18:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save COLTstreet/44d1bfec7edb0e60aacb35841f625e53 to your computer and use it in GitHub Desktop.
Save COLTstreet/44d1bfec7edb0e60aacb35841f625e53 to your computer and use it in GitHub Desktop.
Zoomable Sunburst on d3.js v4 with labels
license: gpl-3.0

Serhii Pahuta and Eduard Trott rewrote Mike Bostock's Zoomable Sunburst to newer version 4 of d3.js library. All functionality remains the same with the exception of labels. I've created this fork of their Sunburst visual and added labels.

Click on any arc to zoom in, and click on the center circle to zoom out. Permanent link.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
stroke: #fff;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var width = 960,
height = 700,
radius = (Math.min(width, height) / 2) - 10;
var formatNumber = d3.format(",d");
var x = d3.scaleLinear()
.range([0, 2 * Math.PI]);
var y = d3.scaleSqrt()
.range([0, radius]);
var color = d3.scaleOrdinal(d3.schemeCategory20);
var partition = d3.partition();
var arc = d3.arc()
.startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x0))); })
.endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x1))); })
.innerRadius(function(d) { return Math.max(0, y(d.y0)); })
.outerRadius(function(d) { return Math.max(0, y(d.y1)); });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2) + ")");
var text,
path;
d3.json("/mbostock/raw/4063550/flare.json", function(error, root) {
if (error) throw error;
root = d3.hierarchy(root);
root.sum(function(d) { return d.size; });
svg.selectAll("path")
.data(partition(root).descendants())
.enter().append("g").attr("class", "node");
path = svg.selectAll(".node")
.append("path")
.attr("d", arc)
.style("fill", function(d) { return color((d.children ? d : d.parent).data.name); })
.on("click", click);
text = svg.selectAll(".node")
.append("text")
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.attr("x", function(d) {
return y(d.y0);
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d) {
return d.data.name === "root" ? "" : d.data.name
});
});
function click(d) {
//Hide text while Sunburst transitions
text.transition().attr("opacity", 0);
svg.transition()
.duration(750)
.tween("scale", function() {
var xd = d3.interpolate(x.domain(), [d.x0, d.x1]),
yd = d3.interpolate(y.domain(), [d.y0, 1]),
yr = d3.interpolate(y.range(), [d.y0 ? 20 : 0, radius]);
return function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); };
})
.selectAll("path")
.attrTween("d", function(d) { return function() { return arc(d); }; })
.on("end", function(e, i) {
// check if the animated element's data e lies within the visible angle span given in d
if (e.x0 > d.x0 && e.x0 < d.x1) {
// get a selection of the associated text element
var arcText = d3.select(this.parentNode).select("text");
// fade in the text element and recalculate positions
arcText.transition().duration(750)
.attr("opacity", 1)
.attr("class", "visible")
.attr("transform", function() { return "rotate(" + computeTextRotation(e) + ")" })
.attr("x", function(d) { return y(d.y0); })
.text(function(d) {
return d.data.name === "root" ? "" : d.data.name
});
}
});
}
function computeTextRotation(d) {
return (x((d.x0 + d.x1)/2) - Math.PI / 2) / Math.PI * 180;
}
d3.select(self.frameElement).style("height", height + "px");
</script>
@akosel
Copy link

akosel commented Apr 9, 2017

Hi there! I really appreciate you putting this together. I noticed a minor bug that occurs when you click and then unclick. The label for the first child disappears. To fix this, on https://gist.github.com/COLTstreet/44d1bfec7edb0e60aacb35841f625e53#file-index-html-L91 change:

if (e.x0 > d.x0 && e.x0 < d.x1) {

to:

if (e.x0 > d.x0 && e.x1 < d.x1) {

and that seems to solve the problem. Again, thank you for putting all of this together!

@harrisnl
Copy link

Hi @akosel, this fix is not working for me, i also have the issue of the first child label disappearing.
Working on a fix now, i'll let you know if I found something.

@michael-donat
Copy link

michael-donat commented Jul 1, 2017

@harrisnl did you find a way around disappearing first label?

@a1rb4Ck
Copy link

a1rb4Ck commented Oct 19, 2017

Hej @akosel, @harrisnl, @michael-donat — my humble fix:
if (e.x0 > d.x0 && e.x0 < d.x1) { to if (e.x0 >= d.x0 && e.x0 < d.x1){
It could not work because the first child label has e.x0 == d.x0.
Again, this is a nice work. Many thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment