連載
» 2012年08月01日 00時00分 公開

Webグラフィックをハックする(4):Canvas APIの基礎 (4/5)

[伊藤千光,WebOS Goodies]

アニメーションとその他の機能

 Canvas APIの主要な描画機能はほぼ解説したので、ここからはアニメーションの実現方法や描画以外の機能に注目していきます。特にビットマップに直接アクセスする機能は、JavaScriptベースの利点を生かしたCanvas APIならではの機能といえるでしょう。

 アニメーション

 SVGやCSSと異なり、JavaScriptで画像を描画するCanvas APIには、それ自体にアニメーションを制御する機能はありません。わずかに変化した画像をプログラムで繰り返し描画してアニメーションを実現します。

 JavaScriptでこうした処理を行う場合、従来はsetInterval()で定期的に描画関数を呼び出すのが通例でした。しかし、この方法では非表示の状態(ウィンドウが最小化されている、別のタブがアクティブになっているなど)でも常に描画負荷がかかります。

 そこで、この問題を回避するために各ブラウザで実装が進んでいるのが、requestAnimationFrame()です。このメソッドで描画処理を呼び出せば、CSSやSVGと同等の滑らかなアニメーションを実現しつつ、非表示状態の描画負荷を抑えることが可能になります。Canvas API専用の機能ではなく、DOMのアニメーションでも利用できます。使用方法を以下に示します。

function tick() {
  var finished = false;
 
  // 再描画の処理
  // アニメーションが終了したら
  // finishedをtrueにする
 
  if(!finished)
    requestAnimationFrame(tick);
}
requestAnimationFrame(tick);

 requestAnimationFrame()の動作は、引数として渡したコールバック関数を次回の更新タイミングで「一回だけ」呼び出すというものです。繰り返し処理するためにはコールバック関数の最後で毎回requestAnimationFrame()を呼び出し、コールバック関数を登録する必要があります。従って、アニメーションが終了した際はrequestAnimationFrame()をスキップするだけで次回からコールバック関数は呼ばれなくなります。

 残念ながらrequestAnimationFrame()はまだ標準化されておらず、すべてのブラウザが実装しているわけではありません。実装している場合でも関数名にはベンダープレフィックスが付いています。実際に使用する際は以下のラッパー関数を使うとよいでしょう。

window.requestAnimFrame = (function(){
  return  window.requestAnimationFrame       ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame    ||
          window.oRequestAnimationFrame      ||
          window.msRequestAnimationFrame     ||
          function(callback){
            window.setTimeout(callback, 1000/60);
          };
})();

 上記のラッパー関数を使用して、実際にCanvas APIで画像を回転するアニメーション実装した例を以下に示します。

アニメーション アニメーション 実際のサンプルを見る

 呼び出しごとに一定の角度だけ回転させるのではなく、実時間を使って回転角を算出している点に注意してください。requestAnimationFrame()は呼び出し間隔を指定できないので、間隔によらず一定の速度でアニメーションするように工夫する必要があります。

 ヒットテスト

 Canvas APIを利用してUIを描画する場合、特定の図形がクリックされたかどうかを判別する必要があります。そのために用意されているのが、isPointInPath()です。引数として渡された座標が現在のパスの内部にあればtrueを、そうでなければfalseを返します。

isPointInPath()メソッド isPointInPath()メソッド

 このメソッドを使用して、パスがクリックされた際にalertを表示する例を以下に示します。isPointInPath()に渡す座標はcanvasタグ内のローカル座標になるため、クリックイベントで渡されたクリック位置を変換する必要がある点に注意してください。

isPointInPath()の使用例 isPointInPath()の使用例 実際のサンプルを見る

 描画結果のエクスポート

 Canvas APIで描画した画像は、PNGなどの形式でエクスポートできます。これを実現するのがtoDataURL()メソッドです(描画コンテキストではなく、canvasのDOMオブジェクトのメソッドです)。

toDataURL()メソッド toDataURL()メソッド

 引数は出力形式を指定するMIMEタイプです。PNG形式(image/png)は必ずサポートすることになっていますが、JPEGなどサポートはブラウザ依存となっています。

 返り値は文字列で、data:スキームのURLとなっています。引数で指定した形式がサポートされていない場合、出力はPNG形式になります。従って、返り値が /^data:image/png[;,]/ の正規表現にマッチするかどうかで、(PNG以外の)指定した画像形式がサポートされているかどうかを判別できます。

 以下は、canvasタグの描画内容をimgタグで表示する例です。左がcanvasタグ、右がimgタグとなっています。imgタグ側を右クリックすると、画像の保存などのあるimgタグ特有のコンテキストメニューが開くことが確認できるはずです。

toDataURL()の使用例 toDataURL()の使用例 実際のサンプルを見る

 なお、セキュリティ上の理由から、別ドメインの画像を描画したcanvasタグに対してtoDataURL()メソッドを呼び出すと例外が発生します。画像のエクスポートが必要なときは、別ドメインの画像を描画しないように注意してください。

 ビットマップの操作

 PNGなどの形式ではなく、素のビットマップに直接アクセスすることも可能です。それを実現するメソッドを以下に示します(描画コンテキストのメソッドです)。

ビットマップにアクセスするメソッド ビットマップにアクセスするメソッド

 createImageData()は、空の、もしくは指定したImageDataオブジェクトをコピーしたImageDataオブジェクトを生成します。

 getImageData()は描画領域内の指定範囲のビットマップをコピーしたImageDataオブジェクトを生成します。このメソッドは、toDataURL()と同様に別ドメインの画像を描画したcanvasタグに対しては呼び出せません。

 putImageData()は逆に、指定したImageDataオブジェクトの内容を描画領域にコピーします。省略可能な4つの引数を指定すると、その範囲のみがコピーされます。省略時はImageDataオブジェクト全体がコピーされます。

 ImageDataオブジェクトには、width, height, dataの3つの属性があります。width, heightはビットマップの縦横のサイズを示します。dataはビットマップを格納した要素数width×height×4の配列です。

 これらのメソッドを利用すれば、Canvas APIで描画した内容をJavaScriptで自由に加工できます。Canvas APIにはSVGのようなフィルタの機能はありませんが、JavaScriptで実装さえすれば、あらゆるフィルタが実現可能です。以下は画像を描画し、そのRチャンネルとBチャンネルを入れ替える例です。

ビットマップの操作 ビットマップの操作 実際のサンプルを見る

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。