Skip to content

Instantly share code, notes, and snippets.

@SpaceActuary
Last active June 9, 2017 16:49
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 SpaceActuary/b7305b4d7cd4f9a1f0480cae6b5a6fc4 to your computer and use it in GitHub Desktop.
Save SpaceActuary/b7305b4d7cd4f9a1f0480cae6b5a6fc4 to your computer and use it in GitHub Desktop.
Zoomable Pie Chart
license: gpl-3.0
{
"name": "total",
"children": [
{
"name": "A",
"children": [
{
"name": "1",
"children": [
{"name": "x", "size": 15},
{"name": "y", "size": 3},
{"name": "z", "size": 2}
]
},
{"name": "2", "size": 20},
{"name": "3", "size": 15}
]
},
{"name": "B","size": 30},
{"name": "C","size": 15}
]
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
stroke: #fff;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
console.clear()
var width = 960,
height = 480,
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.schemeCategory10);
var arcColor = function(d){ return d3.hsl(color(d.depth)).brighter(d.children ? 0 : 1); }
var partition = d3.partition();
var depth = 0,
showKids = 0;
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(0)
.outerRadius(function(d) {
if(d.depth == 0){
// root node -- alway radius + 10
//console.log("outerRadius1", d.data.name, radius + 10);
return Math.max(0, radius + 10);
} else if (d.depth == depth){
//console.log("outerRadius2", d.data.name, y(d.value) + 5);
return Math.max(0, y(d.value) + 5);
} else if (d.depth > depth && showKids){
//console.log("outerRadius3", d.data.name, y(d.parent.value) + 5);
return Math.max(0, y(d.parent.value) + 5);
} else {
//console.log("outerRadius4", d.data.name, y(d.value));
return Math.max(0, y(d.value));
}
})
function tweenIn(b) {
console.log("tweenZoomIn", b.data.name,
"from: " + (b.parent ? y(b.parent.value) : y(b.value)),
"to: " + Math.max(0, y(b.value) + 5))
var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
return function(t) { return arc(i(t)); };
}
function tweenZoomIn(b) {
console.log("tweenZoomIn", b.data.name,
"from: " + (b.parent ? y(b.parent.value) : y(b.value)),
"to: " + Math.max(0, y(b.value) + 5))
b.outerRadius = Math.max(0, y(b.value) + 5);
var i = d3.interpolate({outerRadius: b.parent ? y(b.parent.value) : y(b.value)}, b);
return function(t) { return arc(i(t)); };
}
function tweenOut(b) {
var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
return function(t) { return arc(i(1-t)); };
}
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2) + ")");
d3.json("data.json", function(error, root) {
if (error) throw error;
root = d3.hierarchy(root);
root.sum(function(d) { return d.size; });
console.log("root", partition(root).descendants(), root.value);
y.domain([0, root.value])
svg.selectAll("path")
.data(partition(root).descendants().filter((d)=>{ return d.depth <= depth;}),
function(d) { return d.data.name; })
.enter().append("path")
.attr("d", arc)
.style("fill", arcColor)
.style("pointer-events", "all")
.style("stroke", arcColor)
.on("click", click)
.append("title")
.text(function(d) { return d.data.name + "\n" + formatNumber(d.value); });
function click(d) {
console.log("old", "depth: " + depth, "showKids: " + (showKids ? 'true' : 'false'))
if(!d.children){
//console.log('X')
// ignore clicks if the member has no children
d = d.parent;
} else {
if(depth == d.depth){ // d is at max current depth
if(showKids == 0){
//console.log('A')
showKids = 1;
} else {
//console.log('B')
showKids = 0;
//depth = depth + 1;
}
} else {
if(showKids == 1){
//console.log('C')
showKids = 0;
depth = d.depth;
} else {
//console.log('D')
showKids = 1;
depth = Math.max(0, depth - 1); // decrease depth by one, up to current depth
}
}
}
console.log("new", "depth: " + depth, "showKids: " + (showKids ? 'true' : 'false'))
//console.log("ancestors", d.ancestors())
//console.log("children", d.children, d.children ? "yes" : "no")
var nodes = null;
if(showKids == 1){
nodes = d3.merge([
d.ancestors(),
d.children
])
} else {
nodes = d.ancestors()
}
nodes.sort(function(a, b){ return b.value - a.value; })
console.log("nodes", nodes) //.sort(function())
//console.log("old x.domain", x.domain())
x.domain([0, d.x1]);
//console.log("new x.domain", x.domain())
//console.log("ancestorsAndChildren", nodes)
var arcs = svg.selectAll("path")
//.data(partition(root).descendants().filter((d)=>{ return d.depth <= depth;}))
.data(nodes, function(d) { return d.data.name; })
var arcsEnter = arcs.enter().append("path")
.style("fill", arcColor)
.style("pointer-events", "all")
.style("stroke", arcColor)
.attr("d", arcZero)
.on("click", click)
arcsEnter.append("title")
.text(function(d) { return d.data.name + "\n" + formatNumber(d.value); });
arcsEnter.transition()
.duration(2000)
.attrTween("d", tweenIn)
arcs.transition()
.duration(2000)
.attrTween("d", tweenZoomIn)
arcs.exit().transition()
.duration(2000)
.attrTween("d", tweenOut)
.transition()
.remove()
/*
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); }; });*/
}
});
d3.select(self.frameElement).style("height", height + "px");
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment