|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<title>"Correctly" Animated Tree</title> |
|
<body> |
|
<script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.5.10/d3.min.js"></script> |
|
<!-- --> |
|
<script> |
|
|
|
window.addEventListener("load", e => { |
|
var timer = setInterval(() => { |
|
|
|
mapTree(api.shift()); |
|
drawTree(); |
|
if(api.length==0) clearInterval(timer); |
|
},250) |
|
}) |
|
|
|
var data = { |
|
tree: {"depth":0,"path":[],"id":0,"children": []} |
|
}; |
|
|
|
var tree = d3.layout.tree() |
|
.size([960, 500]) |
|
|
|
var svg = d3.select("body").append("svg") |
|
.attr("width", 960) |
|
.attr("height", 500) |
|
|
|
var linkContainer = svg.append('g'); |
|
|
|
var diagonal = d3.svg.diagonal(); |
|
|
|
var nodeStyle = { |
|
"stroke-width": '3px', |
|
"fill": "black", |
|
"stroke": "black" |
|
} |
|
var linkStyle = { |
|
"stroke": "black", |
|
"fill": "none", |
|
"stroke-opacity": 1, |
|
} |
|
|
|
function drawTree() { |
|
// duration |
|
var du = 500; |
|
|
|
var nodes = tree.nodes(clone(data.tree)); |
|
|
|
var node = svg.selectAll(".node") |
|
.data(nodes, d => d.id) |
|
|
|
var nodeGroup = node.enter() |
|
.append("g") |
|
.attr("class", "node") |
|
.attr("id", d => 'n'+d.id) // id saved to reference later |
|
.attr("transform", d => "translate(" + d.x + "," + d.y + ")") |
|
.style(nodeStyle) |
|
|
|
nodeGroup.append("circle") |
|
.attr("r", 4) |
|
|
|
// update |
|
node.transition() |
|
.duration(du) |
|
.attr("transform", d => "translate(" + d.x + "," + d.y + ")") |
|
|
|
var link = linkContainer.selectAll(".link") |
|
.data(tree.links(nodes), d => d.source.id + "-" + d.target.id ); |
|
|
|
link.enter().append("path") |
|
.attr("class", "link") |
|
.style(linkStyle) |
|
.attr("d", findSrc) |
|
|
|
link.transition() |
|
.duration(du) |
|
.attr("d", diagonal) |
|
|
|
link.exit().remove(); |
|
node.exit().remove(); |
|
} |
|
|
|
// hack to fix path source location |
|
function findSrc(d,i) { |
|
var src = d3.select('#n'+d.source.id).attr("transform").match(/translate\((.*?),(.*?)\)/); |
|
var o = {x: src[1]|0, y: src[2]|0}; |
|
var o2 = {x: d.target.x, y: d.target.y}; |
|
return diagonal({source: o, target: o2}); |
|
} |
|
|
|
// you can ignore the rest, it's just how I generate the data... |
|
|
|
var treeLookup = {}; |
|
|
|
var treeID = 0; |
|
|
|
function mapTree(node) { |
|
node.children = []; |
|
node.id = ++treeID; |
|
|
|
var path = node.path; |
|
var obj = data.tree.children; |
|
var lku = treeLookup; |
|
|
|
for(var i=0;i<path.length;i++) { |
|
if(lku[path[i]]==null) { |
|
lku[path[i]] = {"__index":obj.length}; |
|
if(i!=path.length-1) { |
|
obj.push({"id":++treeID,"path":path.slice(0,i+1),"children":[],"ghost":1}) |
|
} |
|
else { |
|
obj.push(clone(node)) |
|
} |
|
} |
|
lku = lku[path[i]]; |
|
obj = obj[lku.__index].children |
|
} |
|
} |
|
|
|
var api = [{"index":0,"path":[],"status":200}, |
|
{"index":1,"path":["digital-marketing-services"],"status":200}, |
|
{"index":2,"path":["xmlrpc.php"],"status":405}, |
|
{"index":3,"path":["seo-marketing"],"status":200}, |
|
{"index":4,"path":["social-media-marketing"],"status":200}, |
|
{"index":5,"path":["web-design"],"status":200}, |
|
{"index":6,"path":["digital-marketing-strategy"],"status":200}, |
|
{"index":7,"path":["the-team"],"status":200}, |
|
{"index":8,"path":["about-us"],"status":200}, |
|
{"index":9,"path":["training"],"status":200}, |
|
{"index":10,"path":["contact"],"status":200}, |
|
{"index":11,"path":["blog"],"status":200}, |
|
{"index":12,"path":["blog","why-our-best-clients-fire-us-within-a-year"],"status":200}, |
|
{"index":13,"path":["blog","why-you-dont-need-a-meta-description"],"status":200}, |
|
{"index":14,"path":["blog","digital-account-executive-content-social-pr-job-bristol"],"status":200}, |
|
{"index":15,"path":["blog","the-art-of-the-emoji-using-emojis-for-businesses"],"status":200}, |
|
{"index":16,"path":["blog","category","blogs"],"status":200}, |
|
{"index":18,"path":["blog","category","social-media-advice"],"status":200}, |
|
{"index":19,"path":["blog","category","social-media-marketing"],"status":200}, |
|
{"index":20,"path":["blog","category","funnies"],"status":200}, |
|
{"index":21,"path":["blog","internet-slang-dictionary-2015"],"status":200}, |
|
{"index":22,"path":["blog","category","news-tag"],"status":200}, |
|
{"index":23,"path":["blog","google-analytics-data-protection-at-city-of-bath-college"],"status":200}, |
|
{"index":24,"path":["blog","author","natalienoisylittlemonkey-com"],"status":200}, |
|
{"index":26,"path":["blog","category","talks"],"status":200}, |
|
{"index":27,"path":["blog","author","nicnoisylittlemonkey-com"],"status":200}, |
|
{"index":28,"path":["blog","ghost-referrer-spam"],"status":200}, |
|
{"index":29,"path":["blog","google-analytics-data-protection-at-city-nary-2015"],"status":404}, |
|
{"index":32,"path":["blog","category","reporting-advice"],"status":200}, |
|
{"index":33,"path":["blog","author","stevennoisylittlemonkey-com"],"status":200}, |
|
{"index":34,"path":["blog","social-media-for-beginners"],"status":200}, |
|
{"index":35,"path":["blog","author","jonoisylittlemonkey-com"],"status":200}, |
|
{"index":36,"path":["blog","10-of-the-best-lorem-ipsums"],"status":200}]; |
|
|
|
function clone(data) { |
|
return JSON.parse(JSON.stringify(data)); |
|
} |
|
</script> |