{
const svg = d3.create('svg')
.attr('width', width * 0.8)
.attr('height', 500)
const line = d3.line()
.x(d => d.x)
.y(d => d.y);
if (curvedLine) {
line.curve(d3.curveBasis)
}
const path = svg
.append('path')
.datum( data )
.attr('d', line)
.style('stroke', '#ffb3f0')
.style('stroke-width', 2)
.style('fill', 'transparent');
while(true) {
const length = path.node().getTotalLength();
const int = d3.interpolate(0, length);
const x = (t) => path.node().getPointAtLength(int(t)).x;
const y = (t) => path.node().getPointAtLength(int(t)).y;
const circle = svg.selectAll('circle')
.data([ data[0] ])
.join('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.style('stroke', '#6f0043')
.style('stroke-width', 4)
.style('fill', 'transparent')
.attr('r', 8);
if (animate) {
circle.transition().delay(500).duration(points * 100)
.attrTween('cx', () => x )
.attrTween('cy', () => y );
} else {
circle
.attr('cx', data[position].x)
.attr('cy', data[position].y);
}
yield svg.node();
await Promises.tick(points * 100);
}
}