はじめに
Webの世界はベクタグラフィックスで溢れており、FlashやSVGなど、さまざまなグラフィックスのフォーマットが登場しています。その中でも比較的最近登場したHTML Canvasは、他のベクタグラフィックスシステムと異なる独特の立ち位置にあります。
SVGはどんなプログラムからもレンダリングできる宣言型グラフィックスのファイルフォーマットであり、Flashはある完結したマルチメディアシステム(ブラウザ用のプラグインライブラリと、スクリプト言語の ActionScript、そしてコンテンツ作成ツール群で構成されるシステム)を中心に築かれています。これに対し、HTML CanvasはHTMLそのものです。事実、HTML Canvasは来るべきHTML 5仕様に含まれています。それゆえ、HTML CanvasはDOMツリーに統合されます。つまり、JavaScriptからアクセスできるということです。従って、HTML Canvasを使えば、FlashやSVGのレンダラーで行えることの多くを実現できます。
HTML CanvasはHTMLマークアップと個々のピクセル間のギャップを埋めるものです。これを使うと任意のグラフィックスを描画プリミティブ単位で(またはピクセル単位でも)手際よく描くことができ、しかもJavaScriptからこれを直接操作できます。
本稿では、HTML Canvas(以下、HC)を使って実装された簡単な3Dゲームについて説明します。HCは現在のところ2Dグラフィックス用として設計されていますが、 3Dグラフィックスも最終的には2Dグラフィックスとしてレンダリングされるので、3Dにも問題なく対応できます。また、HCはネイティブに実装されるので、かなり高いフレームレートが得られます(この3Dゲームのソースコードは本稿冒頭のリンクからダウンロードできます)。

まず、HCの基本的な事項(セットアップ、描画、使い方)を押さえておきましょう。
HTML Canvasの基本事項
HCへの理解を深めるために、HCで使われる基本モデルを検討しましょう。グラフィックスプリミティブのレンダリングには多くのパラメータが関係します。そのすべてが特定の関数のパラメータであるとするなら、その関数は次のような形式で記述されることになるでしょう。
function drawLine( x, y, width, dotted_style, end_cap_style, transparency, clip_rect, anti_alias, anti_alias_depth, use_graphics_hardware_if_available, ... ... ... );
ご想像のとおり、このスタイルは扱いやすくありません。グラフィックスAPIの多くが、状態マシンと呼ばれるものを使用している理由はここにあります。状態マシンモデルにおいて、dotted_style
、anti_alias
、end_cap_style
といった設定値は、プリミティブ関数のパラメータというよりも、グラフィックスコンテキストの設定値となります。
こうして、必要なパラメータだけ設定し、必要ないパラメータは無視することが可能になります。また、次のように設定値を複数のプリミティブにまとめて適用することもできます。
context.fillStyle = "#f00"; context.strokeStyle = "#0f0"; context.fillRect( 20, 20, 60, 60 ); context.fillRect( 120, 120, 60, 60 ); context.strokeRect( 120, 120, 60, 60 );
保存と復元
HCの状態マシンモデルの利点が不利に働く場面もあります。fillStyle
のようなパラメータを設定すると、その設定はそれ以後に描画されるすべてのプリミティブに影響します。タイピングの手間が大いに省かれる反面、コードの一部分の設定が他の部分に影響するという頭痛の種を増やすことになるのです。
この功罪相半ばの対立構造は、save()
メソッドとrestore()
メソッドを対で使うことにより解決されます。 save()
メソッドで現在のグラフィックスの設定値が保存され、restore()
メソッドでその設定値が復元されます。その間に行った設定値の変更は、restore()
を呼び出した時点で消滅します。
使用例を次に示します。
function wacky() { context.save(); context.fillStyle = "#abc"; context.globalAlpha = .8; context.lineWidth = 23; context.restore(); } function normal() { context.strokeStyle = "#f00"; context.strokeRect( 0, 0, 50, 50 ); wacky(); context.strokeRect( 50, 50, 50, 50 ); }
関数wacky()
はグラフィックスのコンテキストに一連の風変わりな変更を加え、そのレンダリングの結果はかなり変則的なものになります。しかし、この変更はsave()
呼び出しとrestore()
呼び出しに挟まれているため、wacky()
から復帰した時点ですべて消滅します。従って、wacky()
をnormal()
内の2つのstrokeRect()
呼び出しの間で呼び出すとき、2つの四角形の形態が変化することを心配する必要はありません。
save()
呼び出しとrestore()
呼び出しをネストできることにも注意してください。これらのメソッドでは、グラフィックスのコンテキストが実際にはスタックに格納されるからです。
context.save(); // change state // draw draw draw context.save(); // change state even more // draw draw draw context.restore(); // change state back to the first set of changes // draw draw draw context.restore();
上の1番目と3番目の描画コマンド群は同じコンテキストで実行されます。