サンプル
円を描く
アニメーションを考慮せず、canvasに円を描くことを考えます
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const centerX = 150;
const centerY = 150;
const radius = 100;
const numPoints = 360; // ドットの数
function drawCircle() {
// 描画する関数
function draw(x, y) {
ctx.beginPath();
ctx.arc(x, y, 1, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
ctx.closePath();
}
for (let i = 0; i < numPoints; i++) {
const angle = 2 * Math.PI * i / numPoints;
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
draw(x, y);
}
}
drawCircle();
円をアニメーションで表示
ネットで検索したりAIに作成させたりすると、再帰っぽい感じでrequestAnimationFrame()関数で自分自身を呼び出すコードになります
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const centerX = 150;
const centerY = 150;
const radius = 100;
const numPoints = 360; // ドットの数
let currentPoint = 0;
function drawCircle() {
// 描画する関数
function draw(x, y) {
ctx.beginPath();
ctx.arc(x, y, 1, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
ctx.closePath();
}
if (currentPoint < numPoints) {
const angle = 2 * Math.PI * currentPoint / numPoints;
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
draw(x, y);
currentPoint++;
requestAnimationFrame(drawCircle);
}
}
drawCircle();
ここでプロミスを使うと元のLoop構造を残した形に出来ます
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const centerX = 150;
const centerY = 150;
const radius = 100;
const numPoints = 360; // ドットの数
async function drawCircle() {
// 描画する関数
function draw(x, y) {
ctx.beginPath();
ctx.arc(x, y, 1, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
ctx.closePath();
}
for (let i = 0; i < numPoints; i++) {
const angle = 2 * Math.PI * i / numPoints;
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
await new Promise(resolve => {
requestAnimationFrame(() => {
draw(x, y);
resolve();
});
});
}
}
drawCircle();
アニメーション速度を調整可能にする
さらにもっと発展させて描画Speedをコントロールできるようにします
そのためのAnimationDrawerクラスを考えました
使い方は、コード内のコメントおよびサンプルコードを参照ください
//////////////////////////////////////////////////////////////////////////
/**
* アニメーション付きで描画処理を行うためのクラス。
* 1フレームあたりの描画回数を指定してアニメーションのSpeedを調整可能
* @class AnimationDrawer
* @example
* const drawer = new AnimationDrawer(drawFunc, 2);
* drawer.draw(x, y);
* drawer.flush();
*/
//////////////////////////////////////////////////////////////////////////
class AnimationDrawer {
// 1フレームあたりの描画回数(0の場合はアニメーションなし)
#drawPerFrame;
// 待機中の描画関数の引数をキューイングする配列
#pendingArgs = [];
// 描画関数
#drawFunc;
/**
* コンストラクタ
* @param {function} drawFunc 描画処理を行う関数
* @param {number} drawPerFrame 1フレームあたりの描画回数
* (デフォルトは1、0の場合はアニメーションなし)
*/
constructor(drawFunc, drawPerFrame = 1) {
this.#drawFunc = drawFunc;
this.#drawPerFrame = drawPerFrame;
}
/**
* 描画命令をキューに追加
* @param {...any} args - 描画関数の引数
*/
draw(...args) {
// drawPerFrameが0の場合はアニメーションなし
if (this.#drawPerFrame === 0) {
this.#drawFunc(...args);
return Promise.resolve();
}
this.#pendingArgs.push(args);
if (this.#pendingArgs.length >= this.#drawPerFrame) {
return this.flush();
}
return Promise.resolve();
}
/**
* キューにある描画命令を実行
* @returns {Promise<void>}
*/
flush() {
if (this.#pendingArgs.length === 0) return Promise.resolve();
return new Promise(resolve => {
requestAnimationFrame(() => {
for (const arg of this.#pendingArgs) {
this.#drawFunc(...arg);
}
this.#pendingArgs.length = 0;
resolve();
});
});
}
}
AnimationDrawerクラスを使ったサンプルコード
const DRAW_PER_FRAME = 2; // 1フレームあたりの描画数(速度調整用)
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const centerX = 150;
const centerY = 150;
const radius = 100;
const numPoints = 360; // ドットの数
async function drawCircle() {
// 描画する関数
function draw(x, y) {
ctx.beginPath();
ctx.arc(x, y, 1, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
ctx.closePath();
}
const animationDrawer = new AnimationDrawer(draw, DRAW_PER_FRAME);
for (let i = 0; i < numPoints; i++) {
const angle = 2 * Math.PI * i / numPoints;
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
await animationDrawer.draw(x, y);
}
// DRAW_PER_FRAME引数を省略した(デフォルトの1)場合は不要
await animationDrawer.flush();
}
drawCircle();
ここでお試しが出来ます
See the Pen アニメーションで円を描くサンプル by takanaweb5 (@takanaweb5) on CodePen.
実例
以下の記事のお絵かきロジックのパズルを解く様子のアニメーションにこの手法を使用しています