Skip to content

Instantly share code, notes, and snippets.

@HarryStevens HarryStevens/.block

Last active Dec 19, 2016
Embed
What would you like to do?
Voronoi Drag + Jiggle
license: gpl-3.0
<html>
<head>
<style>
body {
margin: 0;
}
.voronoi {
stroke-width: 1px;
stroke: #3a403d;
}
.dot {
fill: #fff;
fill-opacity: .1;
stroke:#3a403d;
cursor: move;
}
</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]);
var xr = d3.scaleLinear().domain([0, width]).range([0, 100]);
var yr = d3.scaleLinear().domain([height, 0]).range([0, 100]);
// 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); })
.call(d3.drag()
.on("drag", dragged)
);
}
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 dragged(d){
var coordinates = [0, 0];
coordinates = d3.mouse(this);
d.x = xr(coordinates[0]);
d.y = yr(coordinates[1]);
var i = findWithAttr(data, "name", d.name);
data[i] = d;
redraw(data);
}
function findWithAttr(array, attr, value) {
for(var i = 0; i < array.length; i += 1) {
if(array[i][attr] === value) {
return i;
}
}
return -1;
}
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
You can’t perform that action at this time.