|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<head> |
|
|
|
<style> |
|
|
|
body { |
|
margin: 0; |
|
} |
|
|
|
path { |
|
fill-opacity: .75; |
|
stroke: black; |
|
} |
|
|
|
</style> |
|
|
|
<script src="//d3js.org/d3.v4.min.js"></script> |
|
<script src="//d3js.org/d3-scale-chromatic.v1.min.js"></script> |
|
|
|
</head> |
|
|
|
<body></body> |
|
|
|
<script> |
|
|
|
var width = window.innerWidth, |
|
height = window.innerHeight, |
|
samples = [], |
|
minMax; |
|
|
|
var voronoi = d3.voronoi() |
|
.size([width, height]) |
|
|
|
var schemes = ["interpolateBlues", "interpolateGreens", "interpolateGreys", |
|
"interpolateOranges", "interpolatePurples", "interpolateReds", |
|
"interpolateBuGn", "interpolateBuPu", "interpolateGnBu", |
|
"interpolateOrRd", "interpolatePuBuGn","interpolatePuBu", |
|
"interpolatePuRd", "interpolateRdPu", "interpolateYlGnBu", |
|
"interpolateYlGn", "interpolateYlOrBr", "interpolateYlOrRd"] |
|
|
|
// the size of the polygon will determine it's fill color - and later, it's placement along the x axis |
|
var logForColor = d3.scaleLog().range([1,0]), |
|
color = d3[schemes[Math.floor(d3.randomUniform(0,schemes.length)())]], |
|
x = d3.scaleLog().range([0,width]); |
|
|
|
var svg = d3.select("body").append("svg") |
|
.attr("width", width) |
|
.attr("height", height) |
|
|
|
getData(); |
|
|
|
var paths = svg.selectAll("path") |
|
.data(samples) |
|
.enter().append("path") |
|
|
|
paths.attr("transform", "translate(0,0)") |
|
.attr("d", function(d, i) {return getPath(samples[i].sample); }) |
|
.style("fill", function(d, i) {return color(logForColor(samples[i].area));}) |
|
|
|
|
|
function getData() { |
|
|
|
// if loading data for the first time, generate an array of random coordinates |
|
if (!samples.length) { |
|
for (i=0; i<500; i++) {samples[i] = [Math.random() * width, Math.random() * height];} |
|
//otherwise, update the x coordinate based on the polygon's size |
|
} else { |
|
for (i in samples) {samples[i] = [x(samples[i].area), samples[i].point[1]];} |
|
} |
|
|
|
// create new polygons and bind them and their calculated area to the main data array |
|
var voronoiData = voronoi(samples).polygons(); |
|
for (i in voronoiData) { |
|
samples[i] = { |
|
point: samples[i], |
|
sample: voronoiData[i], |
|
area: d3.polygonArea(voronoiData[i]), |
|
} |
|
} |
|
|
|
// set the domain of the scales by finding the min and max area values |
|
minMax = d3.extent(samples, function(d) {return d.area;}); |
|
|
|
logForColor.domain([minMax[1],minMax[0]]) |
|
x.domain(minMax); |
|
|
|
|
|
} |
|
|
|
// create the paths for the polygons |
|
function getPath(points) { |
|
|
|
// redrawing the polygons was causing some unwanted effects when transitioning a |
|
// polygon to a new shape that has more sides. this was because the new points |
|
// would be drawn off screen before transitioning into place. |
|
|
|
// my solution was to default each polygon to having more points than (hopefully) |
|
// necessary, with the extra points placed on top of each other. this is potentially |
|
// not performant as it creates multiple points in the same location. |
|
|
|
var thisPath = "M" + points[0]; |
|
for (n=1; n<15; n++) { |
|
thisPath += "L" + (n < points.length ? points[n] : points[0]) |
|
} |
|
return thisPath; |
|
|
|
} |
|
|
|
var sortShapes = function() { |
|
|
|
color = d3[schemes[Math.floor(d3.randomUniform(0,schemes.length)())]] |
|
|
|
// slide the polygons into order of size along the x-axis according to the log scale |
|
paths.transition().duration(2000) |
|
.attr("transform", function(d, i) { |
|
return "translate(" + (-samples[i].point[0] + x(samples[i].area)) + ",0)" |
|
}) |
|
|
|
// re-calculate the voronoi polygons and their sizes based on the new positions |
|
getData(); |
|
|
|
// transition to the new sizes and shapes |
|
paths.transition().delay(2000).duration(2000) |
|
.attr("transform", "translate(0,0)") |
|
.attr("d", function(d, i) {return getPath(samples[i].sample);}) |
|
.style("fill", function(d, i) {return color(logForColor(samples[i].area));}) |
|
|
|
// loop the sorting process |
|
d3.timeout(sortShapes, 6000); |
|
|
|
} |
|
|
|
|
|
d3.timeout(sortShapes, 2000); |
|
|
|
</script> |