JavaScriptで五円玉催眠のアニメーションを描きます。
// 五円玉とヒモの画像
let coin_img = new Image();
coin_img.src = "coin5yen.png";
/**
* 五円玉催眠を描画する。
* @param {CanvasRenderingContext2D} ctx - 描画先の 2D コンテキスト
* @param {number} x - 描画領域の左上 x 座標
* @param {number} y - 描画領域の左上 y 座標
* @param {number} width - 描画領域の幅
* @param {number} height - 描画領域の高さ
* @param {boolean} [is_blur=false] - モーションブラーをするか?
*/
const draw5YenCoinHypnosis = (ctx, x, y, width, height, is_blur = false) => {
ctx.save(); // 現在の描画状態を保存
// クリッピング領域を設定(指定された矩形領域外には描画しない)
ctx.beginPath();
ctx.rect(x, y, width, height);
ctx.clip();
// 描画領域の中心座標を計算
const cx = x + width / 2, cy = y + height / 2;
// 描画領域の寸法を分析
const maxxy = Math.max(width, height); // 縦横の大きい方
const minxy = Math.min(width, height); // 縦横の小さい方
const avgxy = (maxxy + minxy) / 2; // 平均値(未使用)
if (false) {
// デバッグ用:中心点に赤い十字線を描画
ctx.beginPath();
ctx.moveTo(cx - 10, cy); // 横線の左端
ctx.lineTo(cx + 10, cy); // 横線の右端
ctx.moveTo(cx, cy - 10); // 縦線の上端
ctx.lineTo(cx, cy + 10); // 縦線の下端
ctx.lineWidth = 1;
ctx.strokeStyle = "red";
ctx.stroke();
}
// 五円玉の画像が読み込み済みかチェック
if (coin_img.complete) {
// 五円玉画像の元のサイズを取得
const c_width = coin_img.width, c_height = coin_img.height;
// 画面サイズに合わせて画像を縮小する倍率を計算
const s_width = minxy * 0.2; // 画面の短辺の20%のサイズに
const s_height = s_width * c_height / c_width; // アスペクト比を維持
const ratio = s_width / c_width; // 縮小倍率
ctx.save(); // 座標変換前の状態を保存
// 放射状グラデーションを作成(中心から外側に向かって色が変化)
const rInner = 0; // 内側の半径(中心点)
// 外側の半径は時間経過で拡大縮小(脈動効果)
const rOuter = minxy / 2 * (1 + 0.5 * Math.sin(time / 300));
const g = ctx.createRadialGradient(cx, cy, rInner, cx, cy, rOuter);
g.addColorStop(0, `rgba(0, 0, 0, 100%)`); // 中心は黒
g.addColorStop(1, `rgba(60, 0, 91, 100%)`); // 外側は紺色
// 背景をグラデーションで塗りつぶす
if (is_blur) ctx.globalAlpha = 0.2; // モーションブラーありなら、グローバルアルファを設定
ctx.beginPath();
ctx.rect(x, y, width, height);
ctx.fillStyle = g;
ctx.fill();
if (is_blur) ctx.globalAlpha = 1.0 // 元に戻す
// 座標変換:中心を原点に移動
ctx.translate(cx, cy);
// 画像を縮小
ctx.scale(ratio, ratio);
// 画像の中心が原点に来るように調整
ctx.translate(-c_width/2, -c_height/2);
// 振り子のように左右に揺れる角度を計算
const angle = (Math.PI * 0.15) * Math.sin(time / 200); // ±27度
// ヒモの上端のY座標(画像内での位置)
const dy = 12;
// 回転の支点(ヒモの上端)を原点に移動
ctx.translate(c_width/2, dy);
// 振り子のように回転
ctx.rotate(angle);
// 回転後、再び画像の描画位置に戻す
ctx.translate(-c_width/2, -dy);
// すべての座標変換が適用された状態で五円玉画像を描画
ctx.drawImage(coin_img, 0, 0, c_width, c_height);
ctx.restore(); // 座標変換を元に戻す
}
ctx.restore(); // 最初に保存した描画状態を復元
};