Skip to content

Instantly share code, notes, and snippets.

@sim0nf
Forked from mbostock/.block
Created August 10, 2012 20:42
Show Gist options
  • Save sim0nf/3317686 to your computer and use it in GitHub Desktop.
Save sim0nf/3317686 to your computer and use it in GitHub Desktop.
Dynamic Node-Link Tree (D3)

This is an example of building a tree layout using the Reingold-Tilford "tidy" algorithm, as described in "Tidier Drawings of Trees". As each new element is added to the graph, it animates in, starting at the previous position of the parent node. Thus, the existing nodes and the new node transition smoothly to their new positions. The animation stops when 500 nodes have been added to the tree.

Built with D3.js.

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Node-Link Tree</title>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js"></script>
<style type="text/css">
.node {
stroke: #fff;
stroke-width: 2px;
}
.link {
fill: none;
stroke: #000;
}
</style>
</head>
<body>
<script type="text/javascript">
var sfid = 1000;
var w = 960,
h = 500,
root = {},
data = [root],
tree = d3.layout.tree().size([w - 20, h - 20]),
diagonal = d3.svg.diagonal(),
duration = 250,
timer = setInterval(update, duration);
var vis = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(10, 10)");
vis.selectAll("circle")
.data(tree(root))
.enter().append("svg:circle")
.attr("class", "node")
.attr("r", 3.5)
.attr("cx", x)
.attr("cy", y);
function update() {
if (data.length >= 100) {
data.splice(50, 1);
}
// Add a new datum to a random parent.
var d = {id: "ohai"+(++sfid)}, parent = data[~~(Math.random() * data.length)];
if (parent.children) parent.children.push(d); else parent.children = [d];
data.push(d);
// Compute the new tree layout. We'll stash the old layout in the data.
var nodes = tree(root);
// Update the nodes…
var node = vis.selectAll("circle.node")
.data(nodes, nodeId);
// Enter any new nodes at the parent's previous position.
node.enter().append("svg:circle")
.attr("class", "node")
.attr("r", 3.5)
.attr("cx", function(d) { return d.parent.data.x0; })
.attr("cy", function(d) { return d.parent.data.y0; })
.transition()
.duration(duration)
.attr("cx", x)
.attr("cy", y);
node.exit().remove();
// Transition nodes to their new position.
node.transition()
.duration(duration)
.attr("cx", x)
.attr("cy", y);
// Update the links…
var link = vis.selectAll("path.link")
.data(tree.links(nodes), linkId);
// Enter any new links at the parent's previous position.
link.enter().insert("svg:path", "circle")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: d.source.data.x0, y: d.source.data.y0};
return diagonal({source: o, target: o});
})
.transition()
.duration(duration)
.attr("d", diagonal);
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
}
function linkId(d) {
return d.source.data.id + "-" + d.target.data.id;
}
function nodeId(d) {
return d.data.id;
}
function x(d) {
return d.data.x0 = d.x;
}
function y(d) {
return d.data.y0 = d.y;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment