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
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> |