D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
saadkhalid90
Full window
Github gist
voronoi regions limited with circle regions (with position transitions)
<!DOCTYPE html> <head> <meta charset="utf-8"> <script src="https://d3js.org/d3.v4.min.js"></script> <style> body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } </style> </head> <body> <div id=buttonContain> <button id="update">update</button> </div> <script> // Initializaing width and height of svg var width = 960, height = 500 // setting up the svg var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height) var x_pad = 20; var y_pad = 30; var x = d3.scaleLinear() .domain([0, 1]) .range([0, width]); var y = d3.scaleLinear() .domain([0, 1]) .range([0, height]) var n_points = 100 var data = d3.range(n_points).map(function(){ return [Math.random(), Math.random()]; }); svg.selectAll('circle') .data(data) .enter() .append('circle') .attr('r', 3) .attr('cx', d => x(d[0])) .attr('cy', d => y(d[1])) .attr('fill', 'black') .attr('opacity', 0.7) .attr('class', (d, i) => 'index' + i) .classed("points", true); console.log(data) var nodes = data; var voronoi = d3.voronoi() .x(d => x(d[0])) .y(d => y(d[1])) .extent([[0, 0], [width, height]]) console.log(voronoi.polygons(nodes)); console.log(voronoi(nodes)); // var polygon = svg.append('g') // .attr('class', 'voronoi_contain') // //.select('path') // .selectAll(".voronoi") // .data(voronoi.polygons(nodes)) //Use vononoi() with your dataset inside // .enter().append("path") // .attr("class", "voronoi") // .style("stroke", "#2074A0") //If you want to look at the cells // .style("fill-opacity", 0) // .attr('class', (d, i) => 'index' + i) // .classed('cell', true) // .call(redrawPolygon); var polygon = svg.append("defs") .selectAll(".clip") .data(voronoi.polygons(nodes)) //First append a clipPath element .enter().append("clipPath") .attr("class", "clip") //Make sure each clipPath will have a unique id (connected to the circle element) .attr("id", function(d, i) { return "clip-index" + i; }) //Then append a path element that will define the shape of the clipPath .append("path") .attr("class", "clip-path-circle") .attr("d", function(d) { return "M" + d.join(",") + "Z"; }) .style("stroke", 'grey') .style("stroke-width", 1); //Append larger circles svg.selectAll(".circle-catcher") .data(nodes) .enter().append("circle") .attr("class", function(d,i) { return "circle-catcher " + "index" + i; }) //Apply the clipPath element by referencing the one with the same countryCode .attr("clip-path", function(d, i) { return "url(#clip-" + "index" + i + ")"; }) //Bottom line for safari, which doesn't accept attr for clip-path .style("clip-path", function(d, i) { return "url(#clip-" + "index" + i + ")"; }) .attr("cx", function(d) {return x(d[0]);}) .attr("cy", function(d) {return y(d[1]);}) //Make the radius a lot bigger .attr("r", 50) .style("fill", "grey") .style("opacity", 0.5) .style("pointer-events", "all") //Notice that we now have the mousover events on these circles .on("mouseover", activateHover(100)) .on("mouseout", deactivateHover(100)); function redrawPolygon(polygon) { polygon .attr("d", function(d) { return d ? "M" + d.join("L") + "Z" : null; }) .style("stroke", "#2074A0") //If you want to look at the cells .style("stroke-width", 0.5) .style("fill-opacity", 0) //.attr("id", d => (d != null) ? d.data.seat : "NA") } // svg.selectAll('path.cell').on('mouseover', activateHover(500)); // svg.selectAll('path.cell').on('mouseout', deactivateHover(500)); function activateHover(trans_time){ return function(d, i){ var unique_class = d3.select(this).attr('class').replace("circle-catcher ", "") d3.select("circle" + "." + unique_class) .transition(trans_time) .ease(d3.easeQuad) .attr('r', 5) .attr('stroke', 'black') .attr('stroke-width', 1) .style('opacity', 1); } } function deactivateHover(trans_time){ return function(d, i){ var unique_class = d3.select(this).attr('class').replace("circle-catcher ", "") d3.select("circle" + "." + unique_class) .transition(trans_time) .ease(d3.easeQuad) .attr('r', 3) .attr('stroke', 'black') .attr('stroke-width', 0) .style('opacity', 0.7); } } function update(trans_time){ // update the nodes data = d3.range(n_points).map(function(){ return [Math.random(), Math.random()]; }); nodes = data console.log(nodes); d3.selectAll('circle.points') .data(nodes) .transition() .duration(trans_time) .attr("cx", function(d) {return x(d[0]);}) .attr("cy", function(d) {return y(d[1]);}) var oldVorData = d3.selectAll('defs clipPath.clip').data(); d3.selectAll('defs clipPath.clip') .data(voronoi.polygons(nodes)) .select("path") .data(voronoi.polygons(nodes)) //.attr("d", function(d) { return "M" + d.join(",") + "Z"; }); .transition() .duration(trans_time) .attrTween('d', pathTween(oldVorData)) svg.selectAll(".circle-catcher") .data(nodes) .transition() .duration(trans_time) .attr("cx", function(d) {return x(d[0]);}) .attr("cy", function(d) {return y(d[1]);}) } function pathTween(oldData){ return function(d, i){ return function(t){ pathInterp = d3.interpolate(oldData[i], d); return "M" + pathInterp(t).join(",") + "Z"; } } } d3.select('#buttonContain').on("click", function(){ update(2000) }) ; </script> </body>
https://d3js.org/d3.v4.min.js