Skip to content

Instantly share code, notes, and snippets.

@timelyportfolio
Forked from mbostock/.block
Created July 8, 2013 00:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timelyportfolio/5945430 to your computer and use it in GitHub Desktop.
Save timelyportfolio/5945430 to your computer and use it in GitHub Desktop.

Click on any arc to zoom in. Click in the center to zoom out.

A sunburst is similar to a treemap, except it uses a radial layout. The root node of the tree is at the center, with leaves on the circumference. The area (or angle, depending on implementation) of each arc corresponds to its value. Sunburst design by John Stasko. Data courtesy Jeff Heer.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
circle,
path {
cursor: pointer;
}
circle {
fill: none;
pointer-events: all;
}
path {
stroke: #fff;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script>
var margin = {top: 350, right: 480, bottom: 350, left: 480},
radius = Math.min(margin.top, margin.right, margin.bottom, margin.left);
var hue = d3.scale.category10();
var luminance = d3.scale.sqrt()
.domain([0, 1e6])
.clamp(true)
.range([90, 20]);
var svg = d3.select("body").append("svg")
.attr("width", margin.left + margin.right)
.attr("height", margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var partition = d3.layout.partition()
.sort(function(a, b) { return d3.ascending(a.name, b.name); })
.size([2 * Math.PI, radius]);
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx; })
.innerRadius(function(d) { return radius / 3 * d.depth; })
.outerRadius(function(d) { return radius / 3 * (d.depth + 1); });
d3.json("/d/4063550/flare.json", function(error, root) {
// Compute the initial layout on the entire tree to sum sizes.
// Also compute the full name and fill color for each node.
partition
.value(function(d) { return d.size; })
.nodes(root)
.forEach(function(d) { d.sum = d.value; d.key = key(d); d.fill = fill(d); });
// Now redefine the value function to use the previously-computed sum.
partition
.children(function(d, depth) { return depth < 2 ? d.children : null; })
.value(function(d) { return d.sum; });
var center = svg.append("circle")
.attr("r", radius / 3)
.on("click", zoomOut);
center.append("title")
.text("zoom out");
var path = svg.selectAll("path")
.data(partition.nodes(root).slice(1))
.enter().append("path")
.attr("d", arc)
.style("fill", function(d) { return d.fill; })
.each(function(d) { this._current = updateArc(d); })
.on("click", zoomIn);
function zoomIn(p) {
if (p.depth > 1) p = p.parent;
if (!p.children) return;
zoom(p, p);
}
function zoomOut(p) {
if (!p.parent) return;
zoom(p.parent, p);
}
// Zoom to the specified new root.
function zoom(root, p) {
if (document.documentElement.__transition__) return;
// Stash the original root dimensions for transitions.
var angle1 = d3.scale.linear()
.domain([0, 2 * Math.PI])
.range([root.x, root.x + root.dx]);
function insideArc(d) {
return p.key > d.key
? {depth: d.depth - 1, x: 0, dx: 0} : p.key < d.key
? {depth: d.depth - 1, x: 2 * Math.PI, dx: 0}
: {depth: 0, x: 0, dx: 2 * Math.PI};
}
function outsideArc(d) {
return {depth: d.depth + 1, x: angle1(d.x), dx: angle1(d.x + d.dx) - angle1(d.x)};
}
// When zooming in, arcs enter from the outside and exit to the inside.
// When zooming out, arcs enter from the inside and exit to the outside.
var enterArc = root === p ? outsideArc : insideArc,
exitArc = root === p ? insideArc : outsideArc;
center.datum(root);
d3.transition().duration(750).each(function() {
path = path.data(partition.nodes(root).slice(1), function(d) { return d.key; });
path.transition()
.attrTween("d", function(d) { return arcTween.call(this, updateArc(d)); });
path.enter().append("path")
.style("fill-opacity", 0)
.style("fill", function(d) { return d.fill; })
.on("click", zoomIn)
.each(function(d) { this._current = enterArc(d); })
.transition()
.style("fill-opacity", 1)
.attrTween("d", function(d) { return arcTween.call(this, updateArc(d)); });
path.exit().transition()
.style("fill-opacity", 0)
.attrTween("d", function(d) { return arcTween.call(this, exitArc(d)); })
.remove();
});
}
});
function key(d) {
var k = [], p = d;
while (p.depth) k.push(p.name), p = p.parent;
return k.reverse().join(".");
}
function fill(d) {
var p = d;
while (p.depth > 1) p = p.parent;
var c = d3.lab(hue(p.name));
c.l = luminance(d.sum);
return c;
}
function arcTween(b) {
var i = d3.interpolate(this._current, b);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
function updateArc(d) {
return {depth: d.depth, x: d.x, dx: d.dx};
}
d3.select(self.frameElement).style("height", margin.top + margin.bottom + "px");
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment