スタートページJavascriptCANVASアニメーション

アニメーション requestAnimationFrame


requestAnimationFrame の特徴

遅延効果表示をするには、setInterval/clearInterval、async/await, Promise などがありますが、これらは利用者が sleep 時間を指定します。それに対して requestAnimationFrame は、ブラウザがフレームを更新するタイミング(通常16ms)で、画面を更新する仕組みです(時間指定の機能もありますが、ここでは割愛します。
 そのため、フレーム更新を邪魔することなくスムーズな画面推移ができるので、アニメーションに適しています。


例1 基本構造

例1-1 図形の水平移動

例1-2 経過の消去

例1-3 移動の向きの速度と変更

説明

例1-1 1図形を1方向に平行移動 背景経過消去なし

    // ======== canvas 設定 (通常の記述)
    var canvas = document.getElementById("canvas1");
    var ctx = canvas.getContext("2d");
    var cw = canvas.width;
    var ch = canvas.height;
    // ======== 移動図形の初期値
    var x = 30;
    var y = 100;
    var dx = 2;
    // ======== シミュレーション
    function loop() {
        // ======= 移動の方向 Xの方向
        x = x + dx;
        // ======= 移動後の図形の設定
        ctx.beginPath();
        ctx.fillStyle = 'green';
        ctx.arc(x, y, 30, 0, Math.PI*2.0, true);
        ctx.fill();
        // ========= シミュレーションの実行
        var ID = requestAnimationFrame(loop);
        // ========= シミュレーションの打切り
        if (x >= 200) cancelAnimationFrame(ID);
    }
    loop();
}

例1-2 経過の消去

    function loop() {
        ctx.clearRect(0 ,0, cw, ch);  //★ キャンバス全体のクリア
          :
    }

例1-3 移動の向きと速度の変更

    function loop() {
        ctx.clearRect(0 ,0, cw, ch); //キャンバス全体のクリア
        // ======= 変位・向きの変更
        if      ((x < cw-30)  && (y <= 30))    { x = x + 2;  y = 30    } // 右へ
        else if ((x >= cw-30) && (y < ch-30))  { y = y + 5;  x = cw-30 } // 下へ
        else if ((x > 30)     && (y <= ch-30)) { x = x - 3;  y = ch-30 } // 左へ
        else if ((x <= 30)    && (y > 30))     { y = y - 1;  x = 30    } // 上へ
     :
        var ID = requestAnimationFrame(loop);
        if ( (x <= 30) && (y > 30) && (y < 35) ) cancelAnimationFrame(ID);
    }

例2 背景(固定画面)の保存

例2-1 背景画面

例2-2 背景画面の保存

説明

例2-2 背景画面の保存

    // ======== 背景の設定
        :
    var imgData = ctx.getImageData(0, 0, cw, ch);   // ★ 背景の保存

    function loop() {
        ctx.clearRect(0 ,0, cw, ch); //キャンバス全体のクリア
        ctx.putImageData(imgData, 0, 0);  // ★ 背景の復元
         :
    }

例3 複数の画面の移動

例3-1 一つの loop に複数の移動画面を記述する

例3-2 各画面に独立の loop を設定

説明

例3-1 一つの loop に複数の移動画面を記述する

    function loop() {
        ctx.clearRect(0,0, cw, ch); //キャンバス全体のクリア
        ctx.putImageData(imgData, 0, 0);  // 背景の復元
        // ======= 図形1(緑円)の設定
        x1 = x1 + dx1;
         : 
         :  中心 (x1,y1) の緑円
         : 
        // ======= 図形2(赤円)の設定
        x2 = x2 + dv2;
        y2 = y2 - dy2;
         : 
         :  中心 (x2,y2) の赤丸
         : 
        // ========= シミュレーション
        var ID = requestAnimationFrame(loop);
        if ( (x1 >= cw-30) && (y2 <= 30) ) cancelAnimationFrame(ID);  // ★ 打切時刻は同一
    }
    loop();

例3-2 各画面に独立の loop を設定

   // ================================ 図形1(緑円)
  :
    function loop1() {
        // ★ 終了時刻が後の loop
        ctx.clearRect(0 ,0, cw, ch); // キャンバス全体のクリア
        ctx.putImageData(imgData, 0, 0);  // 背景の復元
        // ======= 図形の設定
        x1 = x1 + dx1;
    :
        // ========= シミュレーション
        var ID1 = requestAnimationFrame(loop1);
        if (x1 >= cw-30) cancelAnimationFrame(ID1);
    }
    loop1();
   // ================================ 図形2
  :
    function loop2() {
        // ★ 終了時刻が後の loop ではクリアと復元はしない
        // ======= 図形の設定
        x2 = x2 + dx2;
        y2 = y2 - dy2;
    :
        // ========= シミュレーション
        var ID2 = requestAnimationFrame(loop2);
        if (y2 <= 20) cancelAnimationFrame(ID2);
    }
    loop2();

例4 画像

毎回再ロードしてください。>/p>

例4-1 平行移動

例4-2 回転

例4-3 平行移動と回転の組合せ


説明

画像の留意点

例4-1 画像の平行移動

    // ======== canvasと背景 設定
      :
    var imgData = ctx.getImageData(0, 0, cw, ch);
    // ======== 画像の設定
    var 画像 = new Image();
    画像.src = "RAF41.png";

    // ======== シミュレーション
    function loop() {
        ctx.clearRect(0 ,0, cw, ch); //キャンバス全体のクリア
        ctx.putImageData(imgData, 0, 0);  // 背景の復元
        // ======= 図形の設定
        x = x + dx;
        ctx.drawImage(画像, x, y);  // ★ 画像の表示
        // ========= シミュレーション
        var ID = requestAnimationFrame(loop);
        if (x >= 250) cancelAnimationFrame(ID);
    }
    loop();

例4-2 画像の回転

    // ======== canvasと背景の設定
       : 
    var imgData = ctx.getImageData(0, 0, cw, ch);
    // ======== 画像の設定
    var 画像 = new Image();
    画像.src = "RAF42.png";
    var 画像width = 283;       // ★ 画像のサイズは既知とする
    var 画像height = 283;
    var 画像x0 = 画像width / 2;  // 画像の中心
    var 画像y0 = 画像height / 2;

    // ======== シミュレーション
    var θ = 0;                 // 角度を変化させる
    var dθ = 1*Math.PI/180;
    function loop() {
        ctx.clearRect(0 ,0, cw, ch); //キャンバス全体のクリア
        ctx.putImageData(imgData, 0, 0);  // ★ 背景の復元
        // ======= 画像回転(★実際にはcanvasの座標系の回転)
        θ = θ + dθ;
        ctx.save();                           // canvas の座標系を保持
        ctx.translate(cw/2, ch/2);                // 回転の中心(画像の中心)にCANVASの原点を移動する
        ctx.rotate(-θ);                          // canvasを -θ 回転する (画像回転とは逆方向)
        ctx.drawImage(画像, -画像x0, -画像y0);    // 画像サイズの半分だけずらして画像を描画する
        ctx.restore();                        // canvas の座標系を元に戻す
        // ======= シミュレーションの開始と終了
        var ID = requestAnimationFrame(loop);
        if (θ >= Math.PI/4) cancelAnimationFrame(ID);
    }
    loop();

例4-3 平行移動と回転の組合せ

    // ======== シミュレーション
    function loop() {
        :
        // ======== 右へ水平移動
        if ( (y == 0) && (x <= cw-画像width)) {
            x = x + dx;
            ctx.drawImage(画像, x, y);
        }
        // ======== 右上隅で回転
        else if ((y == 0) && (x >= cw-画像width) && (θ > -Math.PI/2) ) {
            θ = θ - dθ;   // 時計回り
            ctx.save();
            ctx.translate(cw-画像x0, 画像y0);
            ctx.rotate(-θ);
            ctx.drawImage(画像, -画像x0, -画像y0);
            ctx.restore();
        }
        // ======== 下へ垂直移動
        else if ( (y < ch-画像height) && (θ <= -Math.PI/2 ) ) {
            y = y + dy;
            ctx.drawImage(下向画像, cw-画像width, y);  // ★ 複雑になるので、あらかじめ画像を作成した
        }
        :
    }
    loop();