[HTML5] Canvasでアニメーション

Canvasを利用して簡単なパラパラアニメを作成してみます。 原理としては授業中にノートや教科書の端に描いた絵をパラパラとめくって動かしたのと同じ。Canvasに描画したあとに適当な時間待機後、Canvasをお掃除したあとに少しだけ動かした絵を描画…をひたすら繰り返すという物です。

これを実現するには大雑把に2つの方法がありまして、それぞれとりあげます。

setInterval編

まずは一定時間経過すると特定の処理を実行してくれるsetInterval()を利用したアニメです。若干ぎこちない動きになりますが、考え方も実装も簡単です。

デモ

Canvas内に円を描き、左右に移動させる簡単なサンプルです。円が左右の画面ハジにめり込むと方向が反転します。

ソースコード

<canvas id="board" width="300" height="200"></canvas>

<script>
window.onload = ()=>{
  const board = document.querySelector("#board");
  const ctx = board.getContext("2d");

  // 円の初期値
  let x = 0;    // 円のX座標
  let y = 100;  // 円のY座標
  let r = 30;   // 円の半径

  // 1回のアニメ―ション量
  let step = 8;

  //-------------------------------------
  // アニメーション開始
  //-------------------------------------
  setInterval( () => {
      // canvasの全領域をクリア
      ctx.clearRect(0, 0, board.width, board.height); // 本来は必要な部分だけクリアした方が高速

      // 円を描画
      ctx.beginPath();
      ctx.arc(x, y, r, 0, 2 * Math.PI);    // 円の描画
      ctx.fillStyle = "Orange";    // 塗りつぶす色
      ctx.fill();                  // 塗りつぶし

      //画面の端にぶつかると反転
      if( (x < 0) || (x >= board.width) ){
        step *= -1;
      }

      // 座標を移動(横軸)
      x += step;
    },
    10 );  // 10msec経過する毎に実行する
}
</script>

アニメーションを停止する

setInterval()は戻り値としてIDを返却します。これをclearInterval()に渡すことでタイマーを停止することができます。

let intervalID = setInterval(()=>{
  if( ... ){
     // 特定の条件時にタイマーを停止
     clearInterval(intervalID);
  }
}, 20);

// setIntervalの外でも停止できます
document.querySelector("#button").addEventListener("click", ()=>{
  clearInterval(intervalID);
})

requestAnimationFrame編

テレビやディスプレイは1秒間に決まった回数だけ描画・削除を行っています。 この回数をフレームレートと呼び、単位はfps(frames per second)になります。fpsの値が高いほどヌルヌル動きます。

window.requestAnimationFrame()はこのフレームが切り替わるタイミングに合わせて処理を行ってくれます。そのため前述のsetInterval()よりもスムーズにアニメーションを描画することが可能です。

デモ

アニメーション自体はsetInterval()版と同じです。 若干こちらの方がなめらかですかね。

ソースコード

<canvas id="board" width="300" height="200"></canvas>

<script>
/**
 * 円の管理用オブジェクト
 **/
var Ball = {
  // canvasオブジェクト
  canvas: null,
  ctx: null,

  // 円の座標
  pos: {
    x: 0,     // X初期値
    y: 100,   // Y初期値
    r: 30     // 半径
  },

  // アニメーション
  anime: {
    step: 8   // 1回のアニメーション量
  }
};

//-----------------------------------------
// [event] ページ読み完了
//-----------------------------------------
window.onload = ()=>{
  Ball.canvas = document.querySelector("#board");
  Ball.ctx = Ball.canvas.getContext("2d");

  // アニメーション開始
  update();
}

/**
 * 毎フレーム実行
 */
function update(){
  draw();
  window.requestAnimationFrame(update);
}

/**
 * Canvasへ描画
 */
 function draw(){
  let x = Ball.pos.x;
  let y = Ball.pos.y;
  let r = Ball.pos.r;

  // canvasの全領域をクリア
  Ball.ctx.clearRect(0, 0, Ball.canvas.width, Ball.canvas.height);

  // 円を描画
  Ball.ctx.beginPath();
  Ball.ctx.arc(x, y, r, 0, 2 * Math.PI);    // 円の描画
  Ball.ctx.fillStyle = "Orange";    // 塗りつぶす色
  Ball.ctx.fill();                  // 塗りつぶし

  //画面の端にぶつかると跳ね返る
  if( (x < 0) || (x >= Ball.canvas.width) ){
    Ball.anime.step *= -1;
  }

  // 座標を移動
  Ball.pos.x += Ball.anime.step;
}
</script>

アニメーションを停止する

window.requestAnimationFrame()の戻り値としてIDを返却します。これをwindow.cancelAnimationFrame()に渡すことでアニメーションを停止することができます。

let requestID = null;

function update(){
  draw();
  requestID = window.requestAnimationFrame(update);
}

function draw(){
  .....
}

document.querySelector("#button-stop").addEventListener("click", ()=>{
  // アニメーションを停止
  if(requestID !== null){
    window.cancelAnimationFrame(requestID);
  }
})

ちなみに上記のコードで、停止したアニメーションを再開したいときは再びupdate()を実行するだけですね。

参考ページ