Built with blockbuilder.org
forked from /bycoffe/18441cddeb8fe147b719fab5e30b5d45
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
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;
}
#viewport {
width: 960px;
height: 350px;
}
path {
fill: none;
stroke: #999;
}
path.hidden {
display: none;
}
path.init {
stroke-width: 2;
}
path.piece {
stroke-width: 6;
stroke: none;
}
line.sep {
stroke-width: 2;
stroke: none;
}
</style>
<body>
<h2>Think Oregon Trail as Cards</h2>
<div id="viewport"></div>
<h2>Quest</h2>
<ol id="instructions">
<li data-id="1">Embark on your new adventure</li>
<li data-id="2">Engage the Wampas and Collect gold and other trinkets</li>
<li data-id="3">Pass through the Valley of Many Moons</li>
<li data-id="4">Arrive at New Port Gultch and trade your cargo</li>
<li data-id="5">Return home</li>
</ol>
<h4>Dev Log</h4>
<p>Sept 5 2018<p>
<p>Sept 23 2018 - upgraded to D3 v5<p>
<h4>To Do</h4>
<p>show a party progressing along the trail</p>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script>
// State
const margin = {top: 0, right: 20, bottom: 0, left: 20};
const width = 1300 - margin.left - margin.right;
const height = 350 - margin.top - margin.bottom;
// generates an array of 12 colors but I only need 5
// and each color should have a theme ie forest is green
const colorsGen = d3.schemePaired;
const water = "lightblue";
const beach = "#fdbf6f";
const forest = "#33a02c";
const burn = "#b15928";
const colors = [water,
beach,
forest,
burn,
beach]
console.log(colors);
let pts = [];
const numPts = 7;
// this is interesting but I don't want a random color order but I could possibly re-purpose the random sort for other things
/*
function randomSort(a, b) {
return Math.random() > .5 ? 1 : -1;
}
colors.sort(randomSort);
*/
function getPoints(numPts){
const halfHeight = height / 2;
const heights = [ halfHeight - 50,
halfHeight - 75,
halfHeight - 100,
halfHeight - 25,
halfHeight -10];
function getHeight(height){
return Math.random() * ( height - 100 );
}
let newPts = [];
_.times( numPts, function(num){
newPts.push([num*(width/numPts), heights[num]]);
newPts.push([num*(width/numPts)+50, heights[num]]);
});
return newPts;
}
pts = getPoints(numPts);
const path = d3.line()
.curve(d3.curveCardinal);
const svg = d3.select("#viewport").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
const g = svg.append("g")
.attr("id","world")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
const world = g;
const line = g.append("path")
.attr("class", "hidden init")
.attr("d", path(pts));
const p = line.node();
const pLength = p.getTotalLength();
let cumu = 0;
const sampleInterval = .25;
// Main Functions
function showLine(callback, line) {
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 splitPath() {
const numPieces = 5;
const pieceSizes = [];
const pieces = [];
for (let i=0; i<numPieces; i++) {
pieceSizes.push({i: i, size: Math.floor(Math.random() * 20) + 5});
}
const size = pieceSizes.reduce(function(a, b) {
return a + b.size;
}, 0);
const pieceSize = pLength / size;
pieceSizes.forEach(function(x, j) {
var segs = [];
for (let 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;
}
function drawSegments(pieces) {
function hiliteStoryPoint(i) {
function hilite() {
return this.getAttribute("data-id") === String(i);
}
d3.selectAll("#instructions li")
.classed("highlight", hilite);
}
const lines = g.selectAll("path.piece")
.data(pieces)
.enter().append("path")
.attr("class", "piece")
.attr("d", function(d, i) {
return path(d.segs);
});
// end of each segment there should be a goal icon
const 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);
const drawLineEverySec = 1000;
let tickCount = 1;
lines.transition()
.duration(0)
.delay(function(d, i) {
return i * drawLineEverySec;
})
.style("stroke", function(d, i) {
return colors[i];
})
.tween("tick", function(){
hiliteStoryPoint(tickCount);
tickCount++;
})
.on("end", function(d, i) {
if (i === pieces.length-1) {
// turns off story point hilites
setTimeout( function(){
d3.selectAll("#instructions li")
.classed("highlight", false);
}, drawLineEverySec)
}
})
// seperations
seps.transition()
.duration(0)
.delay(function(d, i) {
return i * drawLineEverySec;
})
.style("stroke-width", "10px")
.style("stroke", "#fff");
}
let hiliteCount = 0;
function defineLine() {
const pieces = splitPath();
const 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});
}
});
});
const 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);
}
});
}
function init(){
showLine(defineLine, line);
}
init();
/* Icon experiment
<svg xmlns="https://www.w3.org/2000/svg" version="1.1" xmlns:xlink="https://www.w3.org/1999/xlink" preserveAspectRatio="none" x="0px" y="0px" width="100px" height="100px" viewBox="0 0 100 100">
<defs>
<g id="Layer0_0_FILL">
<path fill="#009999" stroke="none" d="
M 90 48.05
L 90 32.05 80 32.05 80 16.05 22.05 16.05 22.05 29.05 14.05 29.05 14.05 43.05 22.05 43.05 22.05 71.05 35.05 71.05 35.05 88.05 67 88.05 67 71.05 80 71.05 80 48.05 90 48.05 Z"/>
</g>
</defs>
<g transform="matrix( 1, 0, 0, 1, 0,0) ">
<use xlink:href="#Layer0_0_FILL"/>
</g>
</svg>
*/
</script>
</body>
https://d3js.org/d3.v5.min.js
https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js