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 шт):
Одновременно рисуется потому что цикл вызывает мгновенно три раза 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>
Сделайте задержку между запуском функций
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>
Штош, сам разобрался:
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();
}