Building a Resource Topology Diagram with d3.js
This article explains how to use d3.js to create interactive resource topology diagrams, covering SVG basics, d3 selections, data binding, enter/update/exit patterns, force‑directed layout, path calculations, and various optimizations such as text centering, arrow markers, and intersection handling.
Background
As business resources grow, visualizing the global relationships between assets becomes essential. A resource topology graph provides a clear way to view these connections. This article simplifies the business context and focuses on using d3.js to draw such a graph.
Why Choose d3?
d3.js (Data‑Driven Documents) is a front‑end JavaScript library for data visualization. Unlike chart‑oriented libraries such as highcharts or echarts , d3 excels at custom visualizations like topology graphs. The article also compares d3 with go.js and AntV G6 , highlighting d3’s high extensibility and free license (using the latest v5).
SVG Overview
SVG (Scalable Vector Graphics) is an XML‑based language for describing two‑dimensional vector graphics. All SVG elements must be nested inside an <svg></svg> container, which acts as a canvas with its own coordinate system.
Common SVG Elements
circle – attributes: cx , cy , r
<svg width="500px" height="500px">
<circle cx="60" cy="60" r="30"></circle>
</svg>line – attributes: x1 , y1 , x2 , y2
<svg width="500px" height="500px">
<line x1="10" y1="10" x2="200" y2="200"></line>
</svg>text – attributes: x , y , plus styling via stroke (line color) and fill (text color).
path – a versatile element defined by the d attribute, which contains a series of drawing commands.
Getting Started with d3
d3’s API resembles jQuery: d3.select and d3.selectAll return a selection object that wraps DOM nodes. Data binding is performed with selection.data , and the three core phases are enter , update , and exit .
d3.select('svg')
.append('circle')
.attr('cx', 60)
.attr('cy', 60)
.attr('r', 30)
.append('text')
.text('资源节点');Using selectAll together with data allows batch creation of elements:
const circleData = [
{cx: 30, cy: 30, r: 30, nodeName: '节点1'},
{cx: 30, cy: 100, r: 30, nodeName: '节点2'}
];
d3.select('svg')
.selectAll('circle')
.data(circleData)
.enter()
.append('circle')
.attr('cx', d => d.cx)
.attr('cy', d => d.cy)
.attr('r', d => d.r)
.append('text')
.text(d => d.nodeName);The enter phase creates missing elements, exit removes surplus elements, and update modifies existing ones. The newer selection.join method merges these steps:
d3.select('svg')
.selectAll('circle')
.data(circleData)
.join(
enter => enter.append('circle')
.attr('cx', d => d.cx)
.attr('cy', d => d.cy)
.append('text')
.text(d => d.nodeName),
update => update,
exit => exit.remove()
)
.attr('r', d => d.r);Force‑Directed Layout
For complex graphs, manually positioning nodes is impractical. d3 provides a force‑directed simulation that applies five forces: centering, collision, link (spring), many‑body (charge), and positioning.
const simulation = d3.forceSimulation(nodes)
.force('link', d3.forceLink(links))
.force('collision', d3.forceCollide().radius(RADIUS * 2))
.force('center', d3.forceCenter(containerWidth / 2, containerHeight / 2))
.stop();After stopping the automatic ticking, the layout is manually advanced to a stable state:
for (let i = 0, n = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay())); i < n; i++) {
simulation.tick();
}Drawing the Topology
With node coordinates computed, the graph is rendered:
const svg = d3.select('#svg');
let nodeSelection = svg.selectAll('circle')
.data(nodes)
.join('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', d => d.radius)
.attr('stroke', d => d.strokeColor)
.attr('fill', d => d.fillColor);
let textSelection = svg.selectAll('text')
.data(nodes)
.join('text')
.attr('x', d => d.x)
.attr('y', d => d.y)
.attr('stroke', d => d.strokeColor)
.attr('fill', d => d.fillColor)
.text(d => d.text);
let linkSelection = svg.selectAll('path')
.data(links)
.join('path')
.attr('stroke-width', d => d.strokeWidth)
.attr('stroke', d => d.strokeColor)
.attr('d', d => `M ${d.source.x} ${d.source.y} L ${d.target.x} ${d.target.y}`);Optimizations
Text centering – Instead of SVG text , HTML div elements with flexbox can be used for perfect centering.
.text {
position: 'absolute';
display: 'flex';
align-items: 'center';
justify-content: 'center';
}Arrow markers – Adding a marker-end attribute to paths draws directional arrows.
d3.select('body')
.append('svg')
.append('marker')
.attr('id', 'arrow')
.attr('viewBox', '-10 -10 20 20')
.attr('markerWidth', '20')
.attr('markerHeight', '20')
.attr('orient', 'auto')
.append('path')
.attr('d', 'M-4,-3 L 0,0 L -4, 3')
.attr('fill', '#333');
linkSelection.attr('marker-end', 'url(#arrow)');Intersection handling – To avoid arrows being covered by node circles, the line is trimmed to the points where it intersects the source and target circles using the kld‑intersections library.
import { Intersection, ShapeInfo } from 'kld-intersections';
linkSelection.attr('d', d => {
const { source, target } = d;
const sourceCircle = ShapeInfo.circle({ center: [source.x, source.y], radius: source.r });
const targetCircle = ShapeInfo.circle({ center: [target.x, target.y], radius: target.r });
const path = ShapeInfo.path(`M ${source.x} ${source.y} L ${target.x} ${target.y}`);
const sourceIntersection = Intersection.intersect(sourceCircle, path).points[0];
const targetIntersection = Intersection.intersect(targetCircle, path).points[0];
return `M ${sourceIntersection.x} ${sourceIntersection.y} L ${targetIntersection.x} ${targetIntersection.y}`;
});Conclusion
d3.js is a powerful, data‑driven visualization library. The main challenges lie in assembling and maintaining the underlying data. This tutorial provides a foundation for building resource topology graphs; further enhancements such as drag‑and‑drop, zoom, or Web‑Worker‑based layout can be added as needed.
References
SVG Element Reference – https://g.126.fm/0440kH6
d3.js API – https://g.126.fm/0309ow8
d3.js Force‑Directed Graph – https://g.126.fm/01xjKTu
kld‑intersections – https://g.126.fm/019trY4
Web Worker for Force Layout – https://g.126.fm/02cbZCM
Using d3.js for Resource Topology – https://g.126.fm/00SFXeo
NetEase Game Operations Platform
The NetEase Game Automated Operations Platform delivers stable services for thousands of NetEase titles, focusing on efficient ops workflows, intelligent monitoring, and virtualization.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.