Skip to content

Instantly share code, notes, and snippets.

@michaschwab
Created February 11, 2019 17:02
Show Gist options
  • Save michaschwab/61f51973da0d2fb5b9f96198748bd62a to your computer and use it in GitHub Desktop.
Save michaschwab/61f51973da0d2fb5b9f96198748bd62a to your computer and use it in GitHub Desktop.
Animated, fading line following a path

Animated, fading line following a path

An example of a line that follows a pre-defined SVG path element. The line has a fading end.

The line consists of multiple straight line segments with decreasing opacity. By keeping the length of the segments short, and using enough segments, the line appears line a fluid path, and the fading appears to be continuous.

This is inspired by Martin Viégas and Martin Wattenberg's Wind Map.

<html>
<head>
<style>
body {
background: #000;
}
line {
stroke: #fff;
}
</style>
</head>
<body>
<svg width="1000" height="600">
<rect width="1000" height="600" fill="#000"></rect>
<path id="guide" style="fill:none;stroke:rgba(255,255,255,0.1);stroke-width:10px;"
d="m 50,75 c 0,-35 70,-40 115,-1.9 45,45 115,35 115,-0.62 0,-35 -70,-40 -115,0.93 C 115,115 50,115 50,75 Z"
></path>
<g id="segments">
</g>
</svg>
<script src="//d3js.org/d3.v5.min.js" charset="utf-8"></script>
<script>
let progressPercent = 0;
const numberOfSegments = 5;
let segmentLength = 10;
const guide = document.getElementById('guide');
const guideLength = guide.getTotalLength();
const segments = document.getElementById('segments');
const raf = () => {
progressPercent += 0.4; // Move 0.4% of the guide length per frame.
const progressLength = progressPercent * guideLength / 100;
const segmentData = [];
for(let i = 0; i < numberOfSegments; i++) {
let startDistance = progressLength + segmentLength * (i - numberOfSegments);
let endDistance = progressLength + segmentLength * (i - numberOfSegments + 1);
segmentData.push({
start: guide.getPointAtLength(startDistance % guideLength),
end: guide.getPointAtLength(endDistance % guideLength)
});
}
const segmentLines = d3.select(segments)
.selectAll('line')
.data(segmentData);
const pathsEnter = segmentLines.enter()
.append('line')
.attr('opacity', (d, i) => (i + 1) / numberOfSegments);
pathsEnter.merge(segmentLines)
.attr('x1', d => d.start.x)
.attr('y1', d => d.start.y)
.attr('x2', d => d.end.x)
.attr('y2', d => d.end.y);
requestAnimationFrame(raf);
};
raf();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment