Built with blockbuilder.org
forked from frazboyz's block: fresh block
license: mit |
Built with blockbuilder.org
forked from frazboyz's block: fresh block
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v5.min.js"></script> | |
<style> | |
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
</style> | |
</head> | |
<body> | |
<script> | |
let data = { | |
id: 1, | |
firstName: 'tom', | |
children: [ | |
{ | |
id: 2, | |
firstName: 'Tei', | |
children: [ | |
{ | |
id: 3, | |
firstName: 'Sam', | |
} | |
], | |
}, | |
{ | |
id: 4, | |
firstName: 'Hm', | |
children: [ | |
{ | |
id: 5, | |
firstName: 'Tom', | |
children: [ | |
{ | |
id: 6, | |
firstName: 'Hmmm', | |
children: [ | |
{ | |
id: 9, | |
firstName: 'Lil' | |
} | |
] | |
} | |
] | |
} | |
] | |
}, | |
{ | |
id: 7, | |
firstName: 'Hmmmmm', | |
children: [ | |
{ | |
id: 8, | |
firstName: 'Tommy' | |
}, | |
{ | |
id: 9, | |
firstName: 'Tommy' | |
}, | |
{ | |
id: 10, | |
firstName: 'Tommy' | |
}, | |
{ | |
id: 11, | |
firstName: 'Tommy', | |
children: [ | |
{ | |
id: 12, | |
firstName: 'Tommy' | |
}, | |
{ | |
id: 13, | |
firstName: 'Tommy' | |
}, | |
{ | |
id: 14, | |
firstName: 'Tommy' | |
}, | |
{ | |
id: 15, | |
firstName: 'Tommy', | |
children: [ | |
] | |
}, | |
] | |
}, | |
] | |
} | |
] | |
} | |
// Feel free to change or delete any of the code you see in this editor! | |
var svg = d3.select("body").append("svg") | |
.attr("width", 960) | |
.attr("height", 470); | |
var base = svg | |
.append('g') | |
.attr('transform', `translate(${960 / 2}, 20)`); | |
var tree = d3.tree() | |
.nodeSize([100 + 70, 200 + 50]); | |
var root = d3.hierarchy(data); | |
root.x0 = 0; | |
root.y0 = 470 / 2 | |
function onZoom() { | |
base.attr('transform', d3.event.transform); | |
} | |
var zoom = d3.zoom() | |
.scaleExtent([-5, 40]) | |
.translateExtent([[-50000, -50000], [50000, 50000]]) | |
.on('zoom', onZoom); | |
svg.call(zoom) | |
update(root); | |
function update(source) { | |
var treeData = tree(root); | |
var nodes = treeData.descendants(); | |
var node = base.selectAll('g.node') | |
.data(nodes, (n) => n.data.id); | |
var nodeEnter = node.enter() | |
.append('g') | |
.attr('class', 'node') | |
.attr('fill', '#000') | |
.attr('width', 200 + 50) | |
.attr('height', 100 + 70) | |
.attr('transform', () => `translate(${source.y}, ${source.x})`); | |
nodeEnter.append('rect') | |
.attr('width', 200) | |
.attr('height', 100) | |
.attr('fill', '#000') | |
.attr('rx', 5) | |
.attr('ry', 5) | |
.attr('transform', `translate(0, -${100 / 2})`); | |
node.exit().remove(); | |
var nodeUpdate = nodeEnter.merge(node); | |
nodeUpdate.transition() | |
.duration(200) | |
.attr('transform', (n) => `translate(${n.y}, ${n.x})`) | |
function elbow(d) { | |
const parts = [ | |
`M${d.source.y + 200}, ${d.source.x}`, | |
`H${d.target.y - (50 / 4)}`, | |
`V${d.target.x}`, | |
`M${d.target.y - (50 / 4)}, ${d.target.x}`, | |
`H${d.target.y}`, | |
]; | |
return parts.join(); | |
} | |
// Draw the paths between nodes | |
const link = base.selectAll('path.link') | |
.data(treeData.links(nodes), (n) => `${n.source.data.id}:${n.target.data.id}`); | |
const linkEnter = link.enter() | |
.append('path') | |
.attr('class', 'link') | |
.attr('fill', 'none') | |
.attr('stroke', '#adadad') | |
.attr('d', elbow); | |
const linkUpdate = linkEnter.merge(link); | |
linkUpdate.transition() | |
.attr('d', elbow); | |
link.exit().remove(); | |
} | |
function changeToLineage() { | |
// Find the targets path with dfs | |
const stack = []; | |
let target = null; | |
stack.push(root); | |
while (stack.length) { | |
const current = stack.pop(); | |
if (current.data.id === 13) { // hard coded 7 | |
target = current; | |
break; | |
} | |
if (current.children || current.hiddenChildren) { | |
(current.children || current.hiddenChildren || []).forEach(child => stack.push(child)); | |
} | |
} | |
if (target) { | |
let top = target; | |
let previousTop = null; | |
while (top) { | |
top.hiddenChildren = top.children; | |
if (previousTop) { | |
top.children = [previousTop]; | |
} else { | |
top.children = null; | |
} | |
previousTop = top; | |
top = top.parent; | |
} | |
console.log(root); | |
update(root); | |
} | |
} | |
</script> | |
<button onclick="changeToLineage()">Change to lineage</button> | |
</body> |