Voronoi Overlay
HTML
<script src="//d3js.org/d3.v4.min.js"></script>
CSS
body {
position: relative;
width: 960px;
height: 500px;
}
path {
pointer-events: all;
fill: none;
stroke: #666;
stroke-opacity: 0.2;
}
.active circle {
stroke: #000;
stroke-width: 2px;
}
JavaScript
var svg = d3.select("body").append("svg").attr("width", 960).attr("height", 500);
width = +svg.attr("width"),
height = +svg.attr("height"),
radius = 32;
var filter = svg.append("defs")
.append("filter")
.attr("id", "blur")
.append("feGaussianBlur")
.attr("stdDeviation", 5);
var image = new Image;
image.src = "https://octodex.github.com/images/catstello.png";
image.onload = load;
var circles = d3.range(20).map(function() {
return {
x: Math.round(Math.random() * (width - radius * 2) + radius),
y: Math.round(Math.random() * (height - radius * 2) + radius)
};
});
var color = d3.scaleOrdinal()
.range(d3.schemeCategory20);
var voronoi = d3.voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.extent([[-1, -1], [width + 1, height + 1]]);
var circle = svg.selectAll("g")
.data(circles)
.enter().append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var cell = circle.append("path")
.data(voronoi.polygons(circles))
.attr("d", renderCell)
.attr("id", function(d, i) { return "cell-" + i; })
.style("fill", function(d, i) { return color(i); })
.attr("filter", "url(#blur)");
function load() {
/* svg.append("image")
.attr("xlink:href", this.src)
.attr("width", "100%")
.attr("height", "100%") */
}
function blur() {
filter.attr("stdDeviation", this.value / 5);
}
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
d3.select(this).select("circle").attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
cell = cell.data(voronoi.polygons(circles)).attr("d", renderCell);
}
function dragended(d, i) {
d3.select(this).classed("active", false);
}
function renderCell(d) {
return d == null ? null : "M" + d.join("L") + "Z";
}