Simple demo of D3 (v4) force simulation
<!DOCTYPE html>
<meta charset='utf-8'>
<title>force</title>
<style>
.line {
fill: none;
stroke: blue;
stroke-width: 1.5px;
}
.active {
stroke: black;
stroke-width: 2px;
}
</style>
<svg width="960" height="500"></svg>
<script type="text/javascript" src="d3.min.js"></script>
<body>
<script>
var svg = d3.select("svg"),
w = svg.attr('width'),
h = svg.attr('height');
var data = {
nodes: [
{ name: "A" },
{ name: "B" },
{ name: "C" },
{ name: "D" },
{ name: "E" },
{ name: "F" },
{ name: "G" },
{ name: "H" },
{ name: "I" },
{ name: "J" }
],
links: [
{ source: 0, target: 1 },
{ source: 0, target: 2 },
{ source: 0, target: 3 },
{ source: 0, target: 4 },
{ source: 1, target: 5 },
{ source: 2, target: 5 },
{ source: 3, target: 4 },
{ source: 4, target: 8 },
{ source: 5, target: 9 },
{ source: 7, target: 8 },
{ source: 8, target: 9 }
]
};
var simulation = d3.forceSimulation(data.nodes)
.velocityDecay(0.2)
.force("x", d3.forceX( w / 2 ).strength(0.1))
.force("y", d3.forceY( h / 2 ).strength(0.1))
.force("center", d3.forceCenter( w / 2, h / 2))
.force('link', d3.forceLink().distance(50).links(data.links))
.force("collide", d3.forceCollide())
.force("charge", d3.forceManyBody().strength(-200))
.on("tick", ticked);
var links = svg.selectAll("line")
.data(data.links)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var colors = d3.scaleOrdinal(d3.schemeCategory10)
.domain([0,9]);
var nodes = svg.selectAll("circle")
.data(data.nodes)
.enter()
.append("circle")
.attr("r", 10)
.style("fill", function(d, i) { return colors(i); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
function ticked() {
links.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; });
nodes.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
};
function dragstarted(d) {
simulation.stop()
d3.select(this)
.classed('active', true)
.attr("cx", function(d) { return d.x = d3.event.x; })
.attr("cy", function(d) { return d.y = d3.event.y; });
ticked();
}
function dragged() {
d3.select(this)
.attr("cx", function(d) { return d.x = d3.event.x; })
.attr("cy", function(d) { return d.y = d3.event.y; });
ticked();
}
function dragended() {
d3.select(this).classed('active', false);
simulation.alpha(1).restart();
}
</script>
</body>
</html>