D3's general update pattern with a Voronoi diagram & rapidly updating data.
Last active
September 30, 2022 16:15
-
-
Save HarryStevens/6c4ee7e57df79fbd3137aad5de289cf6 to your computer and use it in GitHub Desktop.
Voronoi Jiggle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: gpl-3.0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<style> | |
body { | |
margin: 0; | |
} | |
.voronoi { | |
stroke-width: 1px; | |
stroke: #3a403d; | |
} | |
.dot { | |
fill: #3a403d; | |
} | |
</style> | |
</head> | |
<body> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/1.2.1/chroma.min.js"></script> | |
<script> | |
var width = window.innerWidth, | |
height = window.innerHeight, | |
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".split(""), | |
duration = 12, // milliseconds of transition duration | |
n = 5, // amount of random movement | |
r = [], | |
data = [], | |
c = chroma.scale(["#fc8d62", "#ffd92f", "#66c2a5", "#8da0cb"]).domain([0, width * height / alphabet.length * 2]); // color scale | |
for (var i = n * -1; i <= n; i++){ | |
r.push(i / 125); | |
} | |
alphabet.forEach(function(d){ | |
data.push({name: d, x: random(0, 100), y: random(0, 100)}) | |
}); | |
// scales | |
var x = d3.scaleLinear().domain([0, 100]).range([0, width]); | |
var y = d3.scaleLinear().domain([0, 100]).range([height, 0]); | |
// wrapper | |
var svg = d3.select("body").append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
// voronoi tesselation | |
var voronoi = d3.voronoi() | |
.x(function(d) { return x(d.x); }) | |
.y(function(d) { return y(d.y); }) | |
.extent([[0, 0], [width, height]]); | |
function redraw(data){ | |
// transition | |
var t = d3.transition() | |
.duration(duration); | |
// JOIN | |
var voronoiGroup = svg.selectAll(".voronoi") | |
.data(voronoi(data).polygons(), function(d){ return d.data.name; }); | |
var circle = svg.selectAll(".dot") | |
.data(data, function(d){ return d.name; }); | |
// EXIT | |
voronoiGroup.exit().remove(); | |
circle.exit().remove(); | |
// UPDATE | |
voronoiGroup | |
.transition(t) | |
.attr("d", function(d) { return d ? "M" + d.join("L") + "Z" : null; }) | |
.style("fill", function(d){ return c(area(d) * -1); }); | |
circle | |
.transition(t) | |
.attr("cx",function(d){ return x(d.x); }) | |
.attr("cy",function(d){ return y(d.y); }); | |
// ENTER | |
voronoiGroup.enter().append("path") | |
.attr("class", "voronoi") | |
.attr("d", function(d) { return d ? "M" + d.join("L") + "Z" : null; }) | |
.style("fill", function(d){ return c(area(d) * -1); }) | |
circle.enter().append("circle") | |
.attr("class", "dot") | |
.attr("r", 5) | |
.attr("cx",function(d){ return x(d.x); }) | |
.attr("cy",function(d){ return y(d.y); }); | |
} | |
redraw(data); | |
d3.interval(function() { | |
data.forEach(function(d,i){ | |
d.x = d.x + (1 * r[random(0, r.length - 1)]); | |
d.y = d.y + (1 * r[random(0, r.length - 1)]); | |
if (d.x < 0){ | |
d.x = 0; | |
} else if (d.x > 100){ | |
d.x = 100; | |
} | |
if (d.y < 0){ | |
d.y = 0; | |
} else if (d.y > 100){ | |
d.y = 100; | |
} | |
data[i] = d; | |
}); | |
redraw(data); | |
}, duration * 2); | |
/*FUNCTIONS*/ | |
function random(min, max) { | |
return Math.floor(Math.random() * (max - min + 1)) + min; | |
} | |
function shuffle(array) { | |
var m = array.length, t, i; | |
// While there remain elements to shuffle… | |
while (m) { | |
// Pick a remaining element… | |
i = Math.floor(Math.random() * m--); | |
// And swap it with the current element. | |
t = array[m]; | |
array[m] = array[i]; | |
array[i] = t; | |
} | |
return array; | |
} | |
function area(points) { | |
var sum = 0.0; | |
var length = points.length; | |
if (length < 3) { | |
return sum; | |
} | |
points.forEach(function(d1, i1) { | |
i2 = (i1 + 1) % length; | |
d2 = points[i2]; | |
sum += (d2[1] * d1[0]) - (d1[1] * d2[0]); | |
}); | |
return sum / 2; | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment