Published
Edited
Apr 7, 2020
6 stars
Insert cell
md`
# Line Path Animation
- by Sungryeol Park([sungryeolp@gmail.com](mailto:sungryeolp@gmail.com))
`
Insert cell
{
const svg = d3
.create('svg')
.attr('width', width)
.attr('height', 500);
const stage = svg.append('g');
const scaleX = d3.scaleLinear([0, randomData.length - 1], [0, width]);
const scaleY = d3.scaleLinear([0, d3.max(randomData)], [500, 0]);
const line = d3
.line()
.x((d, i) => scaleX(i))
.y(scaleY)
.curve(d3.curveBasis);
stage
.append('path')
.datum(randomData)
.attr('d', line)
.attr('stroke', 'pink')
.attr('fill', 'none')
.attr('id', 'motionPath');

stage
.append('rect')
.attr('id', 'motionObject')
.attr('width', 10)
.attr('height', 10);

stage
.append('animateMotion')
.attr('xlink:href', '#motionObject')
.attr('dur', '10s')
.attr('fill', 'freeze')
.attr('repeatCount', 'indefinite')
.attr('rotate', 'auto-reverse')
.append('mpath')
.attr('xlink:href', '#motionPath');
// <animateMotion
// xlink:href="#car"
// dur="3s"
// begin="0s"
// fill="freeze"
// repeatCount="indefinite"
// rotate="auto-reverse"
// >
// <mpath xlink:href="#motionPath" />
// </animateMotion>

return svg.node();
}
Insert cell
Insert cell
{
const svg = d3
.create('svg')
.attr('width', width)
.attr('height', 500);
const stage = svg.append('g');
const scaleX = d3.scaleLinear([0, randomData.length - 1], [0, width]);
const scaleY = d3.scaleLinear([0, d3.max(randomData)], [500, 0]);
const line = d3
.line()
.x((d, i) => scaleX(i))
.y(scaleY)
.curve(d3.curveBasis);

stage
.selectAll('path')
.data(randomSeed)
.join('path')
.attr('id', d => `path-${d.id}`)
.datum(d => randomData.map((_d, i) => _d + d.seeds[i] - seedMax / 2))
.attr('d', line)
.attr('stroke', 'pink')
.attr('fill', 'none');

const scaleColor = d3.scaleLinear(
[0, randomSeed.length - 1],
['indigo', 'orange']
);

stage
.selectAll('rect')
.data(randomSeed)
.join('circle')
.attr('id', d => `obj-${d.id}`)
.attr('r', 5)
.attr('fill', (d, i) => scaleColor(i));

stage
.selectAll('animateMotion')
.data(randomSeed)
.join('animateMotion')
.each(function(d, i, e) {
const pathId = `path-${d.id}`;
const objId = `obj-${d.id}`;
d3.select(this).call(linkPathAnimation, {
pathId,
objId,
duration: 5 + Math.random() * 5
});
});

if (!lineVisible) stage.selectAll('path').attr('visibility', 'hidden');

return svg.node();
}
Insert cell
seedMax = 10
Insert cell
function linkPathAnimation(node, { pathId, objId, duration }) {
node
.attr('xlink:href', `#${objId}`)
.attr('dur', `${duration}s`)
.attr('fill', 'freeze')
.attr('repeatCount', 'indefinite')
.attr('rotate', 'auto-reverse')
.append('mpath')
.attr('xlink:href', `#${pathId}`);
}
Insert cell
randomSeed = Array.from({ length: 15 }).map(() => ({
id: getRandomId(),
seeds: Array.from({ length: 15 }).map(() => Math.random() * seedMax)
}))
Insert cell
randomData = Array.from({ length: 15 }).map(() => Math.random() * 30)
Insert cell
getRandomId()
Insert cell
d3 = require('d3@5.15.0')
Insert cell
import {checkbox} from "@jashkenas/inputs"
Insert cell
import { getRandomId } from '@rabelais/random-id'
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more