This is an example of how to split an SVG path into an arbitrary number of pieces.
It is the technique used for The winding path to 270 electoral votes
forked from bycoffe's block: Split an SVG path into pieces
xxxxxxxxxx
<meta charset="utf-8">
<style>
body {
font-family: Helvetica;
font-size: 16px;
}
ol li {
margin-bottom: 10px;
}
li.highlight {
background: yellow;
}
pre {
display: inline;
background: lightgray;
}
#chart {
width: 960px;
height: 350px;
}
path {
fill: none;
stroke: #999;
}
path.hidden {
display: none;
}
path.init {
stroke-width: 2;
}
path.piece {
stroke-width: 12;
stroke: none;
}
line.sep {
stroke-width: 4;
stroke: none;
}
</style>
<body>
Ajay
<div id="chart"></div>
<ol id="instructions">
<li data-id="1" class="highlight">Draw an initial path.</li>
<li data-id="2" >Determine how many pieces you want and what percentage of the full path each piece should account for. (Here we're using 20 pieces of random sizes.)</li>
<li data-id="3">Get the location of each section's points along the overall path.</li>
<li data-id="4">Use those points to draw a new path for each section.</li>
</ol>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
function splitPath() {
var numPieces = 20,
pieceSizes = [],
pieces = [];
for (var i=0; i<numPieces; i++) {
pieceSizes.push({i: i, size: Math.floor(Math.random() * 20) + 5});
}
var size = pieceSizes.reduce(function(a, b) {
return a + b.size;
}, 0);
var pieceSize = pLength / size;
pieceSizes.forEach(function(x, j) {
var segs = [];
for (var i=0; i<=x.size+sampleInterval; i+=sampleInterval) {
pt = p.getPointAtLength((i*pieceSize)+(cumu*pieceSize));
segs.push([pt.x, pt.y]);
}
angle = Math.atan2(segs[1][1] - segs[0][1], segs[1][0] - segs[0][0]) * 180 / Math.PI;
pieces.push({id: j, segs: segs, angle: angle});
cumu += x.size;
});
return pieces;
}
var margin = {top: 0, right: 20, bottom: 0, left: 20},
width = 960 - margin.left - margin.right,
height = 350 - margin.top - margin.bottom,
colors = d3.schemeCategory20b,
pts = [],
numPts = 7;
colors.sort(function(a, b) {
return Math.random() > .5 ? 1 : -1;
});
for (var i=0; i<numPts; i++) {
pts.push([i*(width/numPts), 50]);
pts.push([i*(width/numPts), height-50]);
pts.push([i*(width/numPts)+50, height-50]);
pts.push([i*(width/numPts)+50, 50]);
}
var path = d3.line()
.curve(d3.curveCardinal),
svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom),
g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"),
line = g.append("path")
.attr("class", "hidden init")
.attr("d", path(pts)),
p = line.node(),
pLength = p.getTotalLength(),
cumu = 0,
sampleInterval = .25;
function showLine(callback) {
line.classed("hidden", false)
.attr("stroke-dasharray", pLength + " " + pLength)
.attr("stroke-dashoffset", pLength)
.transition()
.duration(1500)
.ease(d3.easeLinear)
.attr("stroke-dashoffset", 0)
.on("end", function() {
callback();
});
}
function drawSegments(pieces) {
d3.selectAll("#instructions li").classed("highlight", function() {
return this.getAttribute("data-id") === "4";
});
var lines = g.selectAll("path.piece")
.data(pieces)
.enter().append("path")
.attr("class", "piece")
.attr("d", function(d, i) {
return path(d.segs);
});
var seps = g.selectAll("line.sep")
.data(pieces)
.enter().append("line")
.attr("class", "sep")
.attr("transform", function(d, i) {
return "translate(" + d.segs[0][0] + "," + d.segs[0][1] + ")rotate(" + (d.angle-90) + " 0 0)";
})
.attr("x1", -12)
.attr("y1", 0)
.attr("x2", 12)
.attr("y2", 0);
lines.transition()
.duration(0)
.delay(function(d, i) {
return i * 250;
})
.style("stroke", function(d, i) {
return colors[i];
})
.on("end", function(d, i) {
if (i === pieces.length-1) {
d3.selectAll("#instructions li").classed("highlight", false);
}
})
seps.transition()
.duration(0)
.delay(function(d, i) {
return i * 250;
})
.style("stroke", "#fff");
}
showLine(function() {
d3.selectAll("#instructions li").classed("highlight", function() {
return this.getAttribute("data-id") === "3";
});
var pieces = splitPath();
var segments = g.selectAll("g.segment")
.data(pieces)
.enter().append("g"),
pts = [];
pieces.forEach(function(x) {
x.segs.forEach(function(seg, i) {
if (i > 0 && i % 2 === 0) {
pts.push({id: x.id, seg: seg});
}
});
});
var dots = g.selectAll("circle")
.data(pts)
.enter().append("circle")
.attr("cx", function(d, i) {
return d.seg[0];
})
.attr("cy", function(d, i) {
return d.seg[1];
})
.style("fill", function(d, i, j) {
return colors[d.id];
})
.attr("r", 0);
dots.transition()
.duration(0)
.delay(function(d, i) {
return i * 10;
})
.attr("r", 3)
.on("end", function(d, i, j) {
if (i === pts.length-1) {
drawSegments(pieces);
}
});
});
</script>
</body>
Modified http://d3js.org/d3.v4.min.js to a secure url
https://d3js.org/d3.v4.min.js