D3.js forceSimulation Link Force
Example program
Try dragging the nodes, shown as circles.
Example code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 v7 force simulation node detail</title>
</head>
<body>
<svg width="800" height="600"></svg>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
// 1. Prepare the data to draw
var width = document.querySelector("svg").clientWidth;
var height = document.querySelector("svg").clientHeight;
var nodeNumber = 30;
var nodesData = [];
for (var i = 0; i < nodeNumber; i++) {
nodesData.push({
"index": i,
"x": width * Math.random(),
"y": height * Math.random(),
"r": 10
});
}
var linksData = [];
var i = 0;
for (var j = i + 1; j < nodeNumber; j++) {
linksData.push({
"source": i,
"target": j,
"l": Math.random() * 150 + 5 + nodesData[i].r + nodesData[j].r
});
}
// 2. Add SVG elements
var link = d3.select("svg")
.selectAll("line")
.data(linksData)
.enter()
.append("line")
.attr("stroke-width", 1)
.attr("stroke", "black");
var node = d3.select("svg")
.selectAll("circle")
.data(nodesData)
.enter()
.append("circle")
.attr("r", function (d) { return d.r })
.attr("fill", "LightSalmon")
.attr("stroke", "black")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
// 3. Configure forceSimulation
var simulation = d3.forceSimulation()
.force("link",
d3.forceLink()
.distance(function (d) { return d.l; })
.strength(0.03)
.iterations(16))
.force("collide",
d3.forceCollide()
.radius(function (d) { return d.r; })
.strength(0.7)
.iterations(16))
.force("charge", d3.forceManyBody().strength(-200))
.force("x", d3.forceX().strength(0.02).x(width / 2))
.force("y", d3.forceY().strength(0.02).y(height / 2));
simulation
.nodes(nodesData)
.on("tick", ticked);
simulation.force("link")
.links(linksData)
.id(function (d) { return d.index; });
// 4. forceSimulation drawing update function
function ticked() {
link
.attr("x1", function (d) { return d.source.x; })
.attr("y1", function (d) { return d.source.y; })
.attr("x2", function (d) { return d.target.x; })
.attr("y2", function (d) { return d.target.y; });
node
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; });
}
// 5. Drag event functions
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>
</body>
</html>
Explanation
1. Prepare the data to draw
var width = document.querySelector("svg").clientWidth;
var height = document.querySelector("svg").clientHeight;
var nodeNumber = 30;
var nodesData = [];
for (var i = 0; i < nodeNumber; i++) {
nodesData.push({
"index": i,
"x": width * Math.random(),
"y": height * Math.random(),
"r": 10
});
}
Use document.querySelector(...) to get the width and height of the svg.
The ID of a node connected by a link can use any key name. In the code above, index is stored in the node data array and specified later. IDs can also be strings instead of numbers.
var linksData = [];
var i = 0;
for (var j = i + 1; j < nodeNumber; j++) {
linksData.push({
"source": i,
"target": j,
"l": Math.random() * 150 + 5 + nodesData[i].r + nodesData[j].r
});
}
Create the link data array, linksData. In the code above, l is defined so each link can have a different length.
2. Add SVG elements
Add SVG elements for links and nodes. The event functions that run when dragging occurs are registered with call(...).
3. Configure forceSimulation
var simulation = d3.forceSimulation()
.force("link",
d3.forceLink()
.distance(function(d) { return d.l; })
.strength(0.03)
.iterations(16)) // <<< Interaction through links
.force("collide",
d3.forceCollide()
.radius(function(d) { return d.r; })
.strength(0.7)
.iterations(16))
.force("charge", d3.forceManyBody().strength(-200))
.force("x", d3.forceX().strength(0.02).x(width / 2))
.force("y", d3.forceY().strength(0.02).y(height / 2));
simulation
.nodes(nodesData)
.on("tick", ticked);
simulation.force("link")
.links(linksData)
.id(function(d) { return d.index; }); // <<< Interaction through links
The following explains the link-based interactions that can be configured in forceSimulation.
“link”: interaction through links
.force("link",
d3.forceLink()
.distance(function(d) { return d.l; })
.strength(0.03)
.iterations(16))
| Function | Description |
|---|---|
distance |
Sets the link length. The link always tries to return to the configured length. The default is 30. |
strength |
Sets the link strength. The default is 1/Math.min(count(link.source), count(link.target)).If each node has one connection, the value is 1. Even if there are two links between the same nodes, the reaction force remains the same. |
iterations |
Sets the number of simulation iterations.Increasing the number of iterations stabilizes the calculation, but increases calculation time. The default is 1. |
simulation.force("link")
.links(linksData)
.id(function(d) { return d.index; });
Specify the indexed key name with id(...). The default is node.index.
4. forceSimulation drawing update function
This function is called for each simulation step. It reflects the calculation result in the SVG element positions so the SVG elements move.
5. Drag event functions
These are the event functions used during dragging.