Skip to content

Instantly share code, notes, and snippets.

@veltman
Last active February 3, 2018 19:15
Show Gist options
  • Save veltman/de68ecd92fbeed9920037b04e3f47c08 to your computer and use it in GitHub Desktop.
Save veltman/de68ecd92fbeed9920037b04e3f47c08 to your computer and use it in GitHub Desktop.
Swoopy US tour w/ canvas

This but with canvas rendering, which is a bit faster/smoother.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<style>
#swoop {
display: none;
}
path {
fill: none;
stroke-linejoin: round;
stroke-linecap: round;
stroke: #444;
stroke-width: 2px;
}
path:last-of-type {
stroke-dasharray: 12,5,1,5;
}
body {
background-color: #e9ddcd;
}
svg, canvas {
position: absolute;
}
</style>
</head>
<body>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="960" height="500">
<path id="swoop" d="M834.4,67.7c-2.2,11.7-5.9,23.4-11.9,33.7c-2.6,4.5-6.4,8-10.5,11.1c-2,1.6-4.1,3-6.2,4.6c-1.8,1.4-3.7,2.7-5,4.5
c-2.4,3.4-3.2,8.1-2.3,12.1c0.5,2.3,1.4,4.6,3.9,5.3c1.3,0.4,2.8,0.3,4.2,0.2c4.3-0.4,12.3-3.5,15.5,0.6c1.4,1.7,1.6,4,1.3,6.1
c-0.3,2.4-1.3,4.2-3.1,5.7c-4,3.4-9.2,4.6-14.4,3.8c-2.9-0.4-5.7-1-8.6-1.4c-3-0.5-6-1-8.8-2.2c0,0-2.1-0.3-2.1-0.3
c-8.4,5.2-0.1,13.3,1.5,20.1c0.4,1.5,0.7,3,1,4.5c0.3,1.2,0.6,2.3,0.4,3.5c-0.7,4.4-5.8,8.2-10.2,7.9c-5-0.4-9.8-4.2-13.6,0.8
c-0.3,0.4-0.5,0.6-0.5,1c0,0.3,0.3,0.7,0.4,1c0.3,0.6,0.7,1.2,1.1,1.8c1.9,2.3,4.7,3.3,6.8,5.3c2.6,2.5,2.8,4.9,4.4,7.8
c0.8,1.5,1.3,3.2,1.5,4.8c0.4,4.2-3.1,8.6-7.4,9c-4.9,0.5-8.8-5.2-13.8-6.3c-11.2-2.5-21.4,2-31.8,5.5c-1.4,1-6.1,2.4-8,3.1
c-2,0.8-3.9,2-5.3,3.7c-2.7,3.2-3.6,7.9-2,11.8c1.4,3.7,4.8,6.2,8.5,7.2c2.3,0.6,4.6,0.9,6.9,1.5c1.9,0.5,3.7,1.2,5.5,2.1
c6.6,3.5,10.1,10.7,10.9,17.9c0.6,5.8-0.5,11.6-1.8,17.2c-1.1,5-2.8,9.9-5,14.6c-2.1,4.5-4.7,8.8-7.7,12.9c-1.5,2.1-3.1,4.1-4.7,6
c-1.6,1.9-3.4,3.6-4.9,5.6c-0.7,0.9-1.3,1.9-2.4,2.4c-7.2,8.4-17.3,15.7-20.4,26.8c-1.8,6.4-0.5,13.2,1.5,19.5
c1.8,5.7,6,11.5,6.5,17.5c0.3,3.3-0.5,6.7-2.7,9.3c-6.2,7.2-17.8,2.2-25.4,0.9c-10.6-1.9-20.8-3.9-29-11.5c-3.3-3-5.7-7.1-8.6-10.6
c-2.7-3.2-5.2-6.5-8.4-9.1c-3.5-2.8-7.9-4.6-12.1-5.6c-4.4-1-9-1-13.4,0c-4.6,1.1-9,3.3-13.2,5.4c-4.2,2.1-8.3,4.4-12.4,6.7
c-4.1,2.3-8.2,4.4-12.3,6.6c-3.9,2.2-7.6,4.6-11.7,6.4c-3,1.3-6.6,2.6-9.9,2c-7.7-1.3-8.6-11.4-9.8-17.5c-2-10.3-5.1-20.8-0.7-31
c3.4-7.8,9-14.5,16.1-19.4c7.4-5.1,15.7-9,24.4-11.3c4.4-1.2,8.8-2,13.3-2.4c5-0.5,10-0.9,14.9-1.9c8.6-1.8,16.4-6,23.2-11.5
c7.2-5.7,13.3-12.8,18.6-20.2c5.4-7.5,10.1-15.8,13.5-24.4c3.4-8.7,5.3-17.9,4.8-27.2c-0.5-9.9-3.3-19.5-7-28.6
c-1.8-4.4-3.8-8.7-6.1-12.9c-1.1-2.1-2.4-4.1-3.6-6.1c-5.6-8.9-16-11.5-20.3,0c-3.4,9-2.1,19-2.9,28.4c-0.7,7.5-2.3,15.1-7.8,20.6
c-2.5,2.6-5.5,4.6-9,5.5c-3.3,0.8-6.7,0.8-10,0.1c-3.3-0.7-6.4-2.1-9-4.2c-3-2.5-4.8-5.9-5.9-9.5c-1.4-4.3-1.7-9-1.1-13.5
c0.4-2.5,0.9-5,1.1-7.6c0.2-2.6,0.4-5.2,0.4-7.8c0.1-4.8,0.1-9.6-0.5-14.3c-0.6-4.5-1.7-8.9-3.3-13.2c-2.9-7.8-7.3-15.4-13.1-21.5
c-5-5.4-11.3-9.4-18.5-10.9c-4.3-0.9-9-1.1-13.4-0.7c-4.6,0.4-8.9,1.9-12.8,4.4c-8.8,5.6-11.8,14.6-11.2,24.6
c0.2,3.5,0.4,6.9,0.7,10.4c0.7,11.7,10.9,22.8,15.1,33.7c1.9,4.9,3.5,9.8,4.8,14.8c1.4,5.3,2.6,10.8,3.2,16.3
c0.3,3,0.5,6.2,0.1,9.3l0.2,3.3c-0.4,7-2.3,13.7-5.5,19.9c-3.3,6.4-8,11.8-12.5,17.4c-2.3,2.9-4.7,5.7-7.5,8.2
c-3.1,2.8-6.4,5.3-9.6,8c-6.3,5.3-12.5,10.8-18.5,16.5c-6,5.6-11.7,11.5-17.4,17.5c-4.9,5.2-9.5,10.8-14.7,15.6
c-5.5,5-12,8.6-19.4,10c-7.3,1.4-15.1,0-21.2-4.4c-5.1-3.7-8.5-9.1-10.6-15c-1.3-3.6-2.1-7.4-2.2-11.2c-0.1-4.1,0.3-8.2,1-12.2
c4.3-24.8,21.4-44,31.9-66.3c4.6-9.8,7.9-20.4,10.3-30.9c1.3-5.5,2.2-11,2.7-16.6c0.5-5.6,0.6-11.2,0.6-16.8
c0.1-11.3-0.3-22.7-2.1-33.9c-0.9-5.3-2-10.6-3.5-15.7c-0.7-2.6-1.5-5.1-2.4-7.6c-0.4-1.3-0.9-2.5-1.4-3.8
c-0.1-0.2-0.5-0.8-0.4-1.1l-4.2-8.3c-1.7-2.2-3.3-4.4-5.1-6.6c-3.6-4.6-7.5-9-11.9-12.8c-9.1-7.8-20.5-12.6-32.1-15.2
c-12.6-2.9-25.7-2.8-38.5-1.9c-12.7,0.9-25.2,4.8-36.8,10c-17.3,7.7-33.2,19.3-46.7,32.5c-5.7,5.5-10.8,12.2-11.5,20.4
c-0.4,4.4,1,8.7,4.2,11.8c3.4,3.3,8.2,4.9,12.9,5.2c7.5,0.6,15.1,0.3,22.6,0.6c12.1,0.4,24.6,2,35.5,7.6
c9.6,4.9,17.1,12.7,21.9,22.3c3.1,6.2,5.4,12.8,7,19.5c1.6,7,2.2,14.1,2.4,21.3c0.5,14.8-0.5,29.7-4.3,44.1
c-1.8,6.9-4.4,13.5-7.8,19.8c-3,5.8-6.5,11.4-10.6,16.5c-4.1,5.1-8.7,9.9-14.1,13.6c-5.4,3.7-11.5,6.3-17.8,8.1
c-5.4,1.5-10.7,1.9-16.2,0.8c-5.4-1.1-10.6-3.3-14.9-6.7c-4.7-3.6-8-8.4-8.9-14.4c-0.5-3.4-0.4-6.8-0.3-10.2
c0.2-3.7,0.7-7.4,1.4-11.1c4.1-21.1,16.9-42.7,2.6-63.1c-4.9-7-12.3-11.9-20.2-14.8c-5.2-1.9-10.6-3.2-16.1-3.4
c-6.4-0.2-12.8,0.9-19,2.5c-5.5,1.4-10.8,3.2-15.8,5.8c-5.1,2.6-9.5,6-13.9,9.5c-4.4,3.6-9,6.9-14.4,9c-2.7,1.1-5.6,1.9-8.5,2.1
c-2.8,0.2-5.6-0.3-8.3-1.2c-4.2-1.4-7.9-3.8-10.9-7.1c-2.9-3.2-5-7.1-6.4-11.2c-4.2-11.7-3.7-24.2-1.2-36.2
c1.2-5.8,2.8-11.3,4.9-16.8c0.4-1.2,0.9-2.4,1.3-3.6c3.3-9.5,7.8-17.5,13.4-25.8c8.4-12.5,18.2-23.8,26.1-36.7
c8.5-13.8,15.1-28.4,19.9-43.7"/>
</svg>
<canvas width="960" height="500"></canvas>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.20/topojson.min.js"></script>
<script>
var context = document.querySelector("canvas").getContext("2d"),
swoop = document.getElementById("swoop"),
totalLength = swoop.getTotalLength(),
numArrows = 8,
group = totalLength / numArrows,
duration = 1800,
arrowLength = Math.round(group * 0.9),
arrowheads = d3.range(numArrows).map(function(d){ return d * group + arrowLength; }),
swoopPath = new Path2D(swoop.getAttribute("d")),
arrowPath = new Path2D("M10,0 L-3,-8 L-3,8 Z");
context.strokeStyle = context.fillStyle = "#d52621";
context.setLineDash([arrowLength, group - arrowLength]);
d3.json("/mbostock/raw/4090846/us.json", function(err, us) {
var filtered = {
type: "GeometryCollection",
geometries: us.objects.states.geometries.filter(function(d) {
return d.id !== 2 && d.id !== 15 && d.id < 60; // No AK, HI, PR
})
};
var inner = topojson.mesh(us, filtered, function(a, b){ return a !== b; }),
outer = topojson.mesh(us, filtered, function(a, b){ return a === b; });
var geoPath = d3.geoPath().projection(d3.geoAlbersUsa().fitExtent([[10, 10],[950, 490]], outer));
d3.select("svg").selectAll(".us")
.data([outer, inner])
.enter()
.append("path")
.attr("class", "us")
.attr("d", geoPath);
d3.timer(update);
});
function update(t) {
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, 960, 500);
var offset = -group * (t % duration) / duration;
context.lineWidth = 5;
context.beginPath();
context.lineDashOffset = offset;
context.stroke(swoopPath);
arrowheads.forEach(function(d){
var l = d - offset;
if (l < 0) {
l = totalLength + l;
} else if (l > totalLength) {
l -= totalLength;
}
var p = pointAtLength(l);
context.setTransform(1, 0, 0, 1, p[0], p[1]);
context.rotate(angleAtLength(l));
context.beginPath();
context.fill(arrowPath);
});
}
function pointAtLength(l) {
var xy = swoop.getPointAtLength(l);
return [xy.x, xy.y];
}
// Approximate tangent
function angleAtLength(l) {
var a = pointAtLength(Math.max(l - 0.01,0)), // this could be slightly negative
b = pointAtLength(l + 0.01); // browsers cap at total length
return Math.atan2(b[1] - a[1], b[0] - a[0]);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment