JSFiddle - React, Tailwind, and code Playground

by Christian Sonne

HTML

<canvas id="c" height="250" width="250"></canvas>

CSS

canvas {outline: 1px red dotted;}

JavaScript

if (CanvasRenderingContext2D.prototype.ellipse == undefined) {
  CanvasRenderingContext2D.prototype.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise) {
    this.save();
    this.translate(x, y);
    this.rotate(rotation);
    this.scale(radiusX, radiusY);
    this.arc(0, 0, 1, startAngle, endAngle, antiClockwise);
    this.restore();
  }
}


// setup
var ctx = c.getContext('2d');
var dpr = window.devicePixelRatio||1;
ctx.scale(dpr, dpr);
c.width *= dpr;
c.height *= dpr;
c.style.height = c.height/2+"px";
c.style.width = c.width/2+"px";
ctx.lineWidth *= dpr;
var start = null;

function draw(timestamp) {
    if (!start)
        start = timestamp;
   	timestamp -= start;
        
    function easeInOut(t) {
		return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
	};
    var phase = Math.PI/2;
    var seconds = 2*1000;
    ctx.clearRect(0, 0, c.width, c.height);
    var ratio = 38.705498/13.525657;
    var completion = easeInOut(Math.min(1, timestamp/seconds));
    var degree = Math.PI/13;
    for (var i=0; i<=12; i++) {
        ctx.beginPath()
        ctx.ellipse(c.width/2, c.height/2, 0.4*c.width, completion*0.4*c.width/ratio, phase+degree*i, 0, 2*Math.PI, true);
        ctx.stroke();
    }
    if (completion < 1)
	    window.requestAnimationFrame(draw);
}

window.requestAnimationFrame(draw);