JSFiddle - React, Tailwind, and code Playground
HTML
<body>
<canvas id='canvas' width='320' height='300'></canvas>
</body>
CSS
canvas { background:gray }
div { width:100px; height:100px; margin-top:100px; border:1px solid black }
JavaScript
function draw_points( ctx, points ) {
// Do nothing if no points
if ( points.length < 1 )
return;
// Draw single point
if ( points.length <= 2 ) {
var b = points[0];
ctx.beginPath();
ctx.arc( b.x, b.y, ctx.lineWidth / 2, 0, Math.PI * 2, true );
ctx.closePath();
ctx.fill();
return;
}
function average(p1, p2) {
return {x:(p1.x+p2.x)/2, y:(p1.y+p2.y)/2};
}
function vectorwise(func, args) {
return {
x: func.apply(this, args.map(function(p){return p.x})),
y: func.apply(this, args.map(function(p){return p.y}))
};
}
function hop(hopper, fencepost, fac) {
return vectorwise(function(p1,p2){return p1-fac*(p2-p1)}, [fencepost, hopper]);
}
var ms=1; // milliseconds
var sec=1000; // 1000 milliseconds
var animationDuration = 2*sec;
var timePerPoint = animationDuration/points.length;
function drawSplineSegment(prevPoint, startPoint, endPoint, nextPoint) {
var controlPoint = average(hop(nextPoint, endPoint, 0.1), hop(prevPoint, startPoint, 0.1));
ctx.beginPath();
ctx.moveTo(startPoint.x, startPoint.y);
ctx.quadraticCurveTo(controlPoint.x, controlPoint.y, endPoint.x, endPoint.y);
ctx.stroke();
ctx.closePath();
}
var extendedPoints = [].concat(
[hop(points[1], points[0], 1)],
points,
[hop(points[points.length-2], points[points.length-1], 1)]
);
var startTime = (new Date()).getTime();
function scheduledTime(i) {
return startTime + timePerPoint*(i+1);
}
function drawSplineSegmentAndWait(i) {
if (i+4 < extendedPoints.length) {
drawSplineSegment.apply(this, extendedPoints.slice(i, i+4));
var now = (new Date()).getTime();
var timeLeft = scheduledTime(i+1) - now;
...