Inspired by https://twitter.com/moebio/status/659020040050757632 from @moebio and the prompt from @stevenstrogatz https://twitter.com/stevenstrogatz/status/658980873077936128
Built with blockbuilder.org
license: mit |
Inspired by https://twitter.com/moebio/status/659020040050757632 from @moebio and the prompt from @stevenstrogatz https://twitter.com/stevenstrogatz/status/658980873077936128
Built with blockbuilder.org
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<style> | |
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
svg { width: 100%; height: 100%; } | |
</style> | |
</head> | |
<body> | |
<script> | |
var margin = {top: 20, right: 10, bottom: 20, left: 10}; | |
var width = 960 - margin.left - margin.right; | |
var height = 500 - margin.top - margin.bottom; | |
var svg = d3.select("body").append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
svg.append("rect") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.attr("fill", "#111111"); | |
var NUM_O_DOGS = 120; | |
var SPEED = 5; | |
var dogs = []; | |
var targets = d3.range(0, NUM_O_DOGS, 1); | |
targets = d3.shuffle(targets); | |
var radius = (height < width) ? height / 2 : width / 2; | |
for (var i = 0; i < NUM_O_DOGS; i++) { | |
var theta = 2 * Math.PI * i / NUM_O_DOGS; | |
var origin = { | |
x: Math.cos(theta) * radius + width / 2, | |
y: Math.sin(theta) * radius + height / 2 | |
} | |
var color = d3.hsl(i / NUM_O_DOGS * 360, 0.8, 0.6).toString(); | |
dogs.push({ | |
target: targets[i], | |
steps: [origin], | |
color: color | |
}); | |
} | |
// Feel free to change or delete any of the code you see! | |
var step = 0; | |
var circle = svg.selectAll("circle") | |
.data(dogs); | |
var paths = svg.selectAll("path") | |
.data(dogs); | |
paths.enter().append("path") | |
.attr("fill", "none") | |
.attr("stroke", function(d) { | |
return d.color; | |
}) | |
.attr("stroke-width", 0.5) | |
.attr("opacity", 0.8); | |
circle.enter().append("circle") | |
.attr("r", 2.5) | |
.attr("fill", function(d) { | |
return d.color; | |
}); | |
var lineFunction = d3.svg.line() | |
.x(function(d) { return d.x; }) | |
.y(function(d) { return d.y; }) | |
.interpolate("linear"); | |
function chase(dog) { | |
var targetDog = dogs[dog.target]; | |
var deltaX = targetDog.steps[step].x - dog.steps[step].x; | |
var deltaY = targetDog.steps[step].y - dog.steps[step].y; | |
var deltaLength = Math.sqrt(deltaX * deltaX + deltaY * deltaY); | |
if (deltaLength < SPEED) { | |
var unitX = deltaX; | |
var unitY = deltaY; | |
} else { | |
var unitX = deltaX / deltaLength * SPEED; | |
var unitY = deltaY / deltaLength * SPEED; | |
} | |
var newCoordinates = { | |
x: dog.steps[step].x + unitX, | |
y: dog.steps[step].y + unitY | |
} | |
dog.steps.push(newCoordinates); | |
} | |
function reset() { | |
targets = d3.shuffle(targets); | |
dogs.forEach(function(dog, i) { | |
dog.target = targets[i]; | |
dog.steps = dog.steps.slice(0,1); | |
}) | |
} | |
function draw(timestamp) { | |
dogs.forEach(chase); | |
circle | |
.attr("cx", function(d) { return d.steps[step].x; }) | |
.attr("cy", function(d) { return d.steps[step].y; }) | |
.attr("r", 2.5); | |
paths | |
.attr("d", function(d) { | |
return lineFunction(d.steps); | |
}) | |
step++; | |
if (step > 150) { | |
reset(); | |
step = 0; | |
} | |
window.requestAnimationFrame(draw); | |
} | |
window.requestAnimationFrame(draw); | |
</script> | |
</body> |