saw a cool animation
https://twitter.com/MirantisIT/status/703363866516828161
wanted to replicate it in the browser
saw a cool animation
https://twitter.com/MirantisIT/status/703363866516828161
wanted to replicate it in the browser
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <style> | |
| body { | |
| background: #000; | |
| } | |
| ellipse | |
| , circle | |
| { | |
| fill: #fff; | |
| } | |
| path { | |
| fill: none; | |
| stroke: #fff; | |
| stroke-linecap: round; | |
| } | |
| .mid { | |
| stroke-width: 4px; | |
| } | |
| .tail { | |
| stroke-width: 2px; | |
| } | |
| </style> | |
| <body> | |
| <script src="//d3js.org/d3.v3.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.5.1/lodash.min.js"></script> | |
| <script> | |
| var width = 200, | |
| height = 150; | |
| var n = 20, | |
| members = n; | |
| var nodes = d3.range(n).map(function() { | |
| var x = Math.random() * width, | |
| y = Math.random() * height; | |
| return { | |
| x: x, | |
| y: y, | |
| vx: Math.random() * 2 - 1, | |
| vy: Math.random() * 2 - 1 | |
| }; | |
| }); | |
| function f(n){ | |
| return { | |
| x: n, | |
| y: n, | |
| vx: 0, | |
| vy: 0 | |
| }; | |
| } | |
| /* | |
| var nodes = | |
| [ | |
| f(0), | |
| f(8), | |
| f(16), | |
| ]; | |
| */ | |
| nodes.forEach(function(e){ | |
| console.log([e.x, e.y]); | |
| }); | |
| var svg = d3.select("body").append("svg") | |
| .attr("width", width) // no clue why i have to double. this api doesn't match units of the drawspace inside the element | |
| .attr("height", height); | |
| var g = svg.selectAll("g") | |
| .data(nodes) | |
| .enter().append("g"); | |
| var head = g.append("circle") | |
| .attr("cx", function (d) { | |
| return d.x; | |
| }) | |
| .attr("cy", function (d) { | |
| return d.y; | |
| }) | |
| .attr("r", 0); | |
| var edgeScale = d3.scale.linear().domain([8, 60]).range([1.5, 0]); | |
| var distance = function(a, b) { | |
| var x = Math.pow(a.x - b.x, 2); | |
| var y = Math.pow(a.y - b.y, 2); | |
| return Math.sqrt(x + y); | |
| }; | |
| var generateEdges = function(){ | |
| return _(nodes).flatMap(function(d){ | |
| return _(nodes).map(function(other){ | |
| return [ | |
| {x: d.x, y: d.y}, | |
| {x: other.x, y: other.y} | |
| ]; | |
| }).value(); | |
| }).value(); | |
| }; | |
| var theEdges = generateEdges(); | |
| var edgeSelection = svg.selectAll("g") | |
| .data(theEdges) | |
| .enter().append("line"); | |
| var updateEdges = function(){ | |
| var combos = generateEdges(); | |
| combos.forEach(function(e, i){ | |
| var up = theEdges[i]; | |
| var a = up[0]; | |
| var b = up[1]; | |
| var srcA = e[0]; | |
| var srcB = e[1]; | |
| a.x = srcA.x; | |
| a.y = srcA.y; | |
| b.x = srcB.x; | |
| b.y = srcB.y; | |
| }); | |
| edgeSelection | |
| .attr("stroke", function(d){ return "white";}) | |
| .attr("stroke-width", function(d){ | |
| var dist = distance(d[0], d[1]); | |
| var w = edgeScale(dist); | |
| w = w < 0.3 ? 0 : w; | |
| return w; | |
| }) | |
| .attr("x1", function(d){ return d[0].x;}) | |
| .attr("y1", function(d){ return d[0].y;}) | |
| .attr("x2", function(d){ return d[1].x;}) | |
| .attr("y2", function(d){ return d[1].y;}); | |
| }; | |
| updateEdges(); | |
| d3.timer(function() { | |
| for (var i = -1; ++i < n;) { | |
| var guy = nodes[i], | |
| dx = guy.vx, | |
| dy = guy.vy, | |
| x = guy.x += dx, | |
| y = guy.y += dy, | |
| speed = Math.sqrt(dx * dx + dy * dy); | |
| // Bounce off the walls. | |
| var negativeLimit = -10; | |
| var pos = 10; | |
| if (x < negativeLimit || x > (width + pos)) guy.vx *= -1; | |
| if (y < negativeLimit || y > (height + pos)) guy.vy *= -1; | |
| head.attr("cx", function(d){ return d.x}) | |
| .attr("cy", function(d){ return d.y}); | |
| } | |
| updateEdges(); | |
| }); | |
| </script> |