Javascript + Canvas: анимация в цикле

Делаю некий проект с анимацией. Возник вопрос, упростил его до самой сути.
Нужно анимировать рисование нескольких окружностей последовательно:
сначала внутренняя, потом следующая и т.д.

Данный код успешно анимирует, но рисование идет одновременно. Во-первых буду признателен за объяснение, почему так. Во-вторых, хотелось бы сделать последовательную анимацию, используя async/await, если это возможно и правильно.

const PI = 3.1415926535;
const DPI = 2 * PI;
const canvasWidth = 400;
const canvasMid = canvasWidth / 2;
let canvas;
let ctx;

function animatePathDrawing(ctx, r, duration) {
    let start = null;
    let step =  function animatePathDrawingStep(timestamp) {
        
        if (start === null)
        start = timestamp;
        let delta = timestamp - start,
        let progress = Math.min(delta / duration, 1);
        ctx.moveTo(canvasMid + r, canvasMid);
        ctx.arc(canvasMid, canvasMid, r, 0, progress * DPI);
        ctx.stroke();
        if (progress < 1) {
            window.requestAnimationFrame(step);
        }
    };
    window.requestAnimationFrame(step);
}

function init() 
{
    canvas = document.getElementById("myCanvas");
    canvas.width = canvasWidth;
    canvas.height = canvasWidth;
    ctx = canvas.getContext("2d");
    ctx.clearRect(0, 0, canvasWidth, canvasWidth);
    ctx.beginPath();

    for (let k=0; k < 3; k++) {
        // ???
        animatePathDrawing(ctx, 100 + k * 10, 2000);
    }
}

Ответы (3 шт):

Автор решения: Rudi

Одновременно рисуется потому что цикл вызывает мгновенно три раза animatePathDrawing. Чтобы была задержка, можно создать счетчик вызовов let count = 0 проверять внутри init() создан ли канвас чтобы не создавать заново, а рисовать в уже созданном. В animatePathDrawing вызывать init(++count) с увеличенным на еденицу счетчиком и проверять состояние счётчика if (count < 5) init(++count); для выполнения определенного количества отрисовок, сейчас установлена отрисовка 5 окружностей.

const PI = Math.PI;
const DPI = 2 * PI;
const canvasWidth = 400;
const canvasMid = canvasWidth / 2;
let canvas;
let ctx;
let count = 0;

function animatePathDrawing(ctx, r, duration) {
  let start = null;
  let step = function animatePathDrawingStep(timestamp) {

    if (start === null) start = timestamp;

    let delta = timestamp - start;
    let progress = Math.min(delta / duration, 1);

    ctx.moveTo(canvasMid + r, canvasMid);
    ctx.arc(canvasMid, canvasMid, r, 0, progress * DPI);
    ctx.stroke();

    if (progress < 1) {

      window.requestAnimationFrame(step);

    }else {

      if (count < 5) init(++count);

    }
  }
  window.requestAnimationFrame(step);
}

function init(k) {

  if (!canvas) {
    canvas = document.getElementById("myCanvas");
    canvas.width = canvasWidth;
    canvas.height = canvasWidth;
    ctx = canvas.getContext("2d");
    ctx.clearRect(0, 0, canvasWidth, canvasWidth);
    ctx.beginPath();
  }

  animatePathDrawing(ctx, 100 + k * 10, 2000);
}
init(++count)
<canvas id="myCanvas"></canvas>

→ Ссылка
Автор решения: Danil Apsadikov

Сделайте задержку между запуском функций

const PI = 3.1415926535;
const DPI = 2 * PI;
const canvasWidth = 400;
const canvasMid = canvasWidth / 2;
let canvas;
let ctx;

function animatePathDrawing(ctx, r, duration) {
  let start = null;
  let step = function animatePathDrawingStep(timestamp) {

    if (start === null) {
      start = timestamp
    };
    let delta = timestamp - start;
    let progress = Math.min(delta / duration, 1);
    ctx.moveTo(canvasMid + r, canvasMid);
    ctx.arc(canvasMid, canvasMid, r, 0, progress * DPI);
    ctx.stroke();
    if (progress < 1) {
      window.requestAnimationFrame(step);
    }
  };
  window.requestAnimationFrame(step);
}

function init() {
  canvas = document.getElementById("myCanvas");
  canvas.width = canvasWidth;
  canvas.height = canvasWidth;
  ctx = canvas.getContext("2d");
  ctx.clearRect(0, 0, canvasWidth, canvasWidth);
  ctx.beginPath();

  for (let k = 0; k < 3; k++) {
    setTimeout(() => {
      animatePathDrawing(ctx, 100 + k * 10, 2000);
    }, 2000 * k)
  }
}

init();
<canvas id="myCanvas"></canvas>

→ Ссылка
Автор решения: lesobrod

Штош, сам разобрался:

function animateCircle(ctx, r, duration) {
    return new Promise(function(resolve, reject) {
        let start = null;
        
        let step =  function animateCircleStep(timestamp) {
            
            if (start === null)
            start = timestamp;
            let delta = timestamp - start,
            progress = Math.min(delta / duration, 1);
            ctx.moveTo(canvasMid + r, canvasMid);
            ctx.arc(canvasMid, canvasMid, r, 0, progress * DPI);
            ctx.stroke();
            if (progress >= 1) {
                resolve('Done');
                } else {
                window.requestAnimationFrame(step);
            }
        };
        window.requestAnimationFrame(step);
    }); 
}

function init() 
{
    canvas = document.getElementById("myCanvas");
    canvas.width =  canvas.height = canvasWidth;
    ctx = canvas.getContext("2d");
    ctx.clearRect(0, 0, canvasWidth, canvasWidth);
    ctx.strokeStyle = 'red';
    ctx.beginPath();
    const drawAllCircles = async () => {
        for (let k of [1,2,3]) {
            const info = await animateCircle(ctx, 100+k*10, 2000);
        }
    };
    drawAllCircles();
}
→ Ссылка