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