Example illustrating the use of both d3-force-attract
and d3-force-cluster
.
Last active
November 15, 2016 07:00
-
-
Save ericsoco/d2d49d95d2f75552ac64f0125440b35e to your computer and use it in GitHub Desktop.
d3-force-cluster + d3-force-attract I
This file contains 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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>d3-force-cluster and d3-force-attract</title> | |
</head> | |
<body> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://unpkg.com/d3-force-attract@latest"></script> | |
<script src="https://unpkg.com/d3-force-cluster@latest"></script> | |
<script> | |
var width = 960, | |
height = 500, | |
padding = 1.5, // separation between same-color nodes | |
clusterPadding = 6, // separation between different-color nodes | |
maxRadius = 12; | |
var n = 200, // total number of nodes | |
m = 10; // number of distinct clusters | |
var color = d3.scaleSequential(d3.interpolateRainbow) | |
.domain(d3.range(m)); | |
// The largest node for each cluster. | |
var clusters = new Array(m); | |
var nodes = d3.range(n).map(function () { | |
var i = Math.floor(Math.random() * m), | |
r = Math.sqrt((i + 1) / m * -Math.log(Math.random())) * maxRadius, | |
d = { | |
cluster: i, | |
radius: r, | |
x: Math.cos(i / m * 2 * Math.PI) * 200 + width / 2 + Math.random(), | |
y: Math.sin(i / m * 2 * Math.PI) * 200 + height / 2 + Math.random() | |
}; | |
if (!clusters[i] || (r > clusters[i].radius)) clusters[i] = d; | |
return d; | |
}); | |
var simulation = d3.forceSimulation() | |
// keep entire simulation balanced around screen center | |
.force('center', d3.forceCenter(width/2, height/2)) | |
// pull toward center | |
.force('attract', d3.forceAttract() | |
.target([width/2, height/2]) | |
.strength(0.01)) | |
// cluster by section | |
.force('cluster', d3.forceCluster() | |
.centers(function (d) { return clusters[d.cluster]; }) | |
.strength(0.5) | |
.centerInertia(0.1)) | |
// apply collision with padding | |
.force('collide', d3.forceCollide(function (d) { return d.radius + padding; }) | |
.strength(0)) | |
.on('tick', layoutTick) | |
.nodes(nodes); | |
var svg = d3.select('body').append('svg') | |
.attr('width', width) | |
.attr('height', height); | |
var node = svg.selectAll('circle') | |
.data(nodes) | |
.enter().append('circle') | |
.style('fill', function (d) { return color(d.cluster/10); }) | |
.call(d3.drag() | |
.on('start', dragstarted) | |
.on('drag', dragged) | |
.on('end', dragended) | |
); | |
function dragstarted (d) { | |
if (!d3.event.active) simulation.alphaTarget(0.3).restart(); | |
d.fx = d.x; | |
d.fy = d.y; | |
} | |
function dragged (d) { | |
d.fx = d3.event.x; | |
d.fy = d3.event.y; | |
} | |
function dragended (d) { | |
if (!d3.event.active) simulation.alphaTarget(0); | |
d.fx = null; | |
d.fy = null; | |
} | |
// ramp up collision strength to provide smooth transition | |
var transitionTime = 3000; | |
var t = d3.timer(function (elapsed) { | |
var dt = elapsed / transitionTime; | |
simulation.force('collide').strength(Math.pow(dt, 2) * 0.7); | |
if (dt >= 1.0) t.stop(); | |
}); | |
function layoutTick (e) { | |
node | |
.attr('cx', function (d) { return d.x; }) | |
.attr('cy', function (d) { return d.y; }) | |
.attr('r', function (d) { return d.radius; }); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment