This is an example of a System DAG using a forceSimulation with images
Last active
May 4, 2021 09:44
-
-
Save IPWright83/932373f52dcc843bb2ccf9a87d14636d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<meta charset="utf-8" /> | |
<title>System Diagram</title> | |
<style> | |
.graph { | |
position: absolute; | |
top: 0; | |
left: 0; | |
} | |
.node-layer { | |
position: absolute; | |
} | |
svg { | |
position: absolute; | |
top: 0; | |
left: 0; | |
} | |
text { | |
font-family: sans-serif; | |
font-size: 12px; | |
text-anchor: middle; | |
user-select: none; | |
} | |
</style> | |
<svg width="1600" height="800"> | |
<defs> | |
<marker | |
id="arrowhead" | |
viewBox="-0 -5 10 10" | |
refX="26" | |
refY="0" | |
orient="auto" | |
markerWidth="15" | |
markerHeight="15" | |
xoverflow="visible" | |
> | |
<path d="M 0,-5 L 9 ,0 L 0,5" fill="#000" /> | |
</marker> | |
</defs> | |
<g class="node-layer" /> | |
<g class="link-layer" /> | |
</svg> | |
<script src="https://d3js.org/d3.v6.min.js"></script> | |
<script> | |
const width = 1600; | |
const height = 800; | |
const systems = [ | |
{ id: 'SystemA', dependencies: [{ id: 'SystemL' }]}, | |
{ id: 'SystemB' }, | |
{ id: 'SystemC', dependencies: [{ id: 'SystemK' }]}, | |
{ id: 'SystemD', dependencies: [{ id: 'SystemE' }]}, | |
{ id: 'SystemE', dependencies: [{ id: 'SystemA' }]}, | |
{ id: 'SystemF', dependencies: [{ id: 'esg-aws-application-load-balancer' }]}, | |
{ id: 'SystemG', dependencies: [{ id: 'SystemI' }]}, | |
{ id: 'esg-aws-application-load-balancer' }, | |
{ id: 'SystemH', dependencies: [{ id: 'SystemM' }]}, | |
{ id: 'SystemI' }, | |
{ id: 'SystemJ', dependencies: [{ id: 'SystemF' },{ id: 'SystemD' },{ id: 'SystemG' },{ id: 'SystemC' },{ id: 'SystemH' }]}, | |
{ id: 'SystemK' }, | |
{ id: 'SystemL', dependencies: [{ id: 'SystemB' }]}, | |
{ id: 'SystemM' }, | |
]; | |
const data = { | |
nodes: systems, | |
links: systems.flatMap((system) => | |
(system.dependencies || []).map((dependency) => ({ | |
...dependency, | |
source: system.id, | |
target: dependency.id, | |
})) | |
), | |
}; | |
// Clear the layers completely first | |
d3.select('.node-layer').selectAll().remove(); | |
d3.select('.node-layer').selectAll().remove(); | |
// Hook up zoom/panning events | |
const svg = d3.select('svg').call( | |
d3.zoom().on('zoom', (event) => { | |
svg.attr('transform', event.transform); | |
}) | |
); | |
// Zoom/Pan handlers | |
const drag = (simulation) => { | |
function dragstarted(event) { | |
if (!event.active) simulation.alphaTarget(0.3).restart(); | |
event.subject.fx = event.subject.x; | |
event.subject.fy = event.subject.y; | |
} | |
function dragged(event) { | |
event.subject.fx = event.x; | |
event.subject.fy = event.y; | |
} | |
function dragended(event) { | |
if (!event.active) simulation.alphaTarget(0); | |
event.subject.fx = null; | |
event.subject.fy = null; | |
} | |
return d3.drag().on('start', dragstarted).on('drag', dragged).on('end', dragended); | |
}; | |
// Create the force layout | |
const simulation = d3 | |
.forceSimulation(data.nodes) | |
.force( | |
'link', | |
d3.forceLink(data.links).id((d) => d.id) | |
) | |
.force('charge', d3.forceManyBody().strength(-1000)) | |
.force('center', d3.forceCenter(width / 2, height / 2)) | |
.on('tick', () => { | |
links | |
.attr('x1', (d) => d.source.x) | |
.attr('y1', (d) => d.source.y) | |
.attr('x2', (d) => d.target.x) | |
.attr('y2', (d) => d.target.y); | |
nodes.attr('transform', (d) => `translate(${d.x},${d.y})`); | |
}); | |
// Add the links to the DOM | |
const links = d3 | |
.select('.link-layer') | |
.selectAll('line') | |
.data(data.links) | |
.enter() | |
.append('line') | |
.attr('class', 'link') | |
.attr('stroke', 'black') | |
.attr('marker-end', 'url(#arrowhead)') | |
.style('pointer-events', 'none'); | |
// Nodes & their labels will move around as a group | |
const nodes = d3 | |
.select('.node-layer') | |
.selectAll('.node') | |
.data(data.nodes) | |
.enter() | |
.append('g') | |
.attr('class', 'node'); | |
// Add the node images | |
nodes | |
.append('svg:image') | |
.attr('class', 'node') | |
.attr('xlink:href', 'https://hackernoon.com/hn-images/1*lAR9Uh_gJ7dp23e0vhy5Hg.png') | |
.attr('x', -25) | |
.attr('y', -25) | |
.attr('height', 50) | |
.attr('width', 50) | |
.call(drag(simulation)); | |
nodes | |
.append('text') | |
.attr('class', 'label') | |
.attr('x', 0) | |
.attr('y', 40) | |
.text((d) => d.name) | |
.style('pointer-events', 'none'); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment