|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<canvas width="800" height="500"></canvas> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script> |
|
|
|
var canvas = document.querySelector("canvas"), |
|
context = canvas.getContext("2d"), |
|
width = canvas.width, |
|
height = canvas.height, |
|
tau = 2 * Math.PI; |
|
|
|
var buildings = d3.range(10).map(function(i) { |
|
return { |
|
x: (-0.5 + Math.random()) * width * 0.8, |
|
y: (-0.5 + Math.random()) * height * 0.8, |
|
}; |
|
}); |
|
|
|
var nodes = d3.range(400).map(function(i) { |
|
return { |
|
r: Math.random() * 8 + 3, |
|
building: Math.floor(Math.random() * buildings.length) |
|
}; |
|
}); |
|
|
|
|
|
function shape(alpha){ |
|
for (var i = 0, n = nodes.length, node, k = alpha * 0.1; i < n; ++i) { |
|
node = nodes[i]; |
|
var dx = node.x - buildings[node.building].x, |
|
dy = node.y - buildings[node.building].y; |
|
node.vx -= dx * k; |
|
node.vy -= dy * k; |
|
} |
|
} |
|
|
|
var simulation = d3.forceSimulation(nodes) |
|
//.velocityDecay(0.2) |
|
.force("shape", shape) |
|
.force("collide", d3.forceCollide().radius(function(d) { return d.r + 0.5; }).iterations(2)) |
|
.on("tick", ticked); |
|
|
|
var color = d3.scaleOrdinal(d3.schemeCategory10); |
|
|
|
function ticked() { |
|
context.clearRect(0, 0, width, height); |
|
context.save(); |
|
context.translate(width / 2, height / 2); |
|
|
|
nodes.forEach(function(d,i) { |
|
context.beginPath(); |
|
context.moveTo(d.x + d.r, d.y); |
|
context.arc(d.x, d.y, d.r, 0, tau); |
|
context.fillStyle = color(d.building); |
|
context.fill(); |
|
context.strokeStyle = "white"; |
|
context.stroke(); |
|
}); |
|
|
|
context.restore(); |
|
} |
|
|
|
</script> |