Canvas と SVG の使い分けに関する考察
本記事は、マイクロソフト本社の IE チームのブログから記事を抜粋し、翻訳したものです。
【元記事】Thoughts on when to use Canvas and SVG (2011/4/23 3:48 AM)
IE9 は、HTML5 の Canvas と SVG という 2 つの優れたグラフィックス機能に対応しています。先週ラスベガスで開催された MIX11 カンファレンスでもこれらに関するセッションが行われました (「HTML5 <canvas> の詳細 ( 英語) 」と「最先端の Web サイトを実現: HTML5 の SVG ( 英語) 」を参照)。
これらのテクノロジを活用することで、最先端の Web のさまざまなグラフィック関連シナリオに対応できるようになります。また、充実した機能を持つ Canvas の陰で忘れられがちな SVG ですが、多くの場面で SVG が最適とされるケースも多いのです。この記事では Canvas、SVG、またその組み合わせの使い分けについて考察したいと思います。
Canvas と SVG の概要
Canvas と SVG に関する概要を以下にまとめました。この概要を枠組みとして、一方のベクター グラフィック テクノロジをもう一方のテクノロジに優先して使用すべき状況について考えていきたいと思います。
Canvas と SVG の比較 | |
Canvas | SVG |
ピクセルベース (Canvas は基本的には、描画 API を備えた画像要素)。 | オブジェクト モデルベース(SVG 要素は HTML の要素に似ている)。 |
<img> に似た動作を持つ 1 つの HTML 要素。 | 複数のグラフィック要素で、DOM (Document Object Model) の一部になる。 |
視覚表現は、スクリプトを使ってプログラム的に作成、変更される。 | 視覚表現は、マークアップを使って作成され、CSS 経由、あるいはスクリプトを使って変更される。 |
イベント モデル/ユーザーの対話操作は canvas 要素のみとなり、粒度が大きい。対話操作はマウス座標から手作業でプログラムする必要がある。 | イベント モデル/ユーザーの対話操作はオブジェクトベース。線、四角形、パスなどプリミティブなレベルの画像要素を使用できる。 |
API がアクセシビリティをサポートしていない。canvas の他にマークアップベースの技術を使う必要がある。 | SVG のマークアップとオブジェクト モデルがアクセシビリティを直接サポート。 |
SVG はインメモリ モデルに準拠した保持モードのグラフィックス モデルとして知られています。HTML と同様、SVG は要素、属性、スタイルのオブジェクト モデルを構築します。HTML5 ドキュメントの <svg> 要素は、インライン ブロックと同様に動作し、HTML ドキュメント ツリーの一部になります。
Canvas は、直接モードのグラフィックス API (Application Programming Interface) を描画用に持つビットマップです。Canvas の場合、グラフィックスをビットマップに直接描画した後は、描画済みの図形はいかなる応答もせず、作成されたビットマップだけが保持されます。この点で Canvas は "撃ち放し (fire and forget)" モデルと呼ぶことができます。
これらを理解するための 1 つの方法として、グラフィックスをプログラム的にウィンドウに描画する Canvas は Windows GDI API に、また、要素、スタイル、イベント、DOM ベースのプログラマビリティを持つ SVG は HTML マークアップ要素に似ていると考えることができます。Canvas は手続き的で、SVG は宣言的だとも言えます。
さまざまなシナリオ
以降のセクションでは、この 2 つのテクノロジが持つ技術的なメリットと制限について説明します。その中で、特定の作業に適しているテクノロジを判断するための一般的なアプローチについても触れていきます。以下の図は Canvas と SVG を両端とする分布図で、中心には両者の明確な分岐点が存在します。この分布図に各シナリオを配置していきます。
高精細/複雑なベクター ドキュメント
高精細で複雑なベクター ドキュメントはこれまで SVG が活躍してきた分野であり、その状況はこれからも続くでしょう。表示および印刷時の非常に細かい表現が特長で、単体で使うことも Web ページに埋め込むことも可能です。宣言型という SVG の特性により、静的に使うことにも、データベースを使ったクライアントまたはサーバー サイドの図形生成にも適しています。
Internet Explorer Test Drive から、実際に使われている図表のデモを紹介します。
1 つ目の画像は図表です。2 つ目の画像はこの図表を 1000% に拡大したものです。
大きな図表で確認できる便利さがあるという点、そしてエンジニアリングを目的とする詳細部分の表示やドキュメント全体の印刷にも対応できるという点から、SVG (Scalable Vector Graphics) の "Scalable (拡大縮小)" のメリットをはっきりと感じることができます。こうした理由から、高精細かつ複雑なベクター ドキュメントは分布図の SVG に近いところに置きましょう。
画像形式としての SVG
SVG のもう 1 つの一般的な用途は、Web ページに静的な画像として配置するというものです。高 DPI モニターが普及している現在、開発者はグラフィックスの品質を考慮する必要があります。以下の画像は、CSS でスタイル設定された <li> の行頭文字イメージの例です。これら 2 つの画像は、見え方もファイル サイズもほぼ同一です。
SVG 画像 (左)、同じ画像の PNG レンダリング (右)
開発者がこの画像を再利用して大きなサイズを作成すると、あるいはエンド ユーザーが高 DPI 画面を使うと、ラスター画像の方はぼやけてしまい、同じ精細度を保つには同じ画像のより大きなバージョンが必要になります。
拡大した SVG 画像 (左)、4000 ピクセルに拡大した PNG 画像(右)
つまり SVG は、Web ページで使われている最もシンプルな画像を置き換えるための形式として適しています。Canvas では、これに相当する置き換えはできません。
分布図の反対側にある Canvas は、描画内容を保持する必要がない場合に高速な処理を発揮します。Canvas が初めて登場した当時は、楽しい作品が数多く開発されたものでした。ここからは、3 つの異なるシナリオについて説明していきたいと思います。
ピクセル操作
ピクセルベースの描画サーフェイスを描画、操作する Canvas には、高度なアルゴリズムを使ってレイ トレースやフィルターなどの印象的な画像効果を適用したさまざまな実験例や使用例があります。
以下の例は Adam Burmister 氏によって作成されたものです。この例では、画像平面上のピクセルで光のパスをトレースして、仮想オブジェクトとの関係性から発生する効果をシミュレートしています。
作者自身が "これは CPU をかなり多く消費するので、ブラウザーが応答していないように見えることがあるかもしれない" と警告しているとおり、Canvas API を使ってこうした画像を生成することはできるものの、それ自体は良いアイデアとはならない可能性があります。同氏はサイト作成者の意見として、"レイ トレースは史上最低の JavaScript アプリケーションだ" と結論付けています。
このことは、画面全体を使用するその他のピクセル操作にも当てはまります。以下の関数は、ある Canvas の緑のピクセルを、同じサイズが設定された別の Canvas のピクセルに置き換えるというものです。こうした関数は、ビデオの "グリーン スクリーン" 効果の作成などに使用できます。
function GreenScreenAtoB(a, b) {
var aImageData = a.getImageData(0, 0, a.canvas.width, a.canvas.height);
var bImageData = b.getImageData(0, 0, b.canvas.width, b.canvas.height);
var aPixels = aImageData.data;
var bPixels = bImageData.data;
if (aPixels.length != bPixels.length) {
window.alert("Canvases do not have the same number of pixels");
return bImageData;
}
var pixelCount = bPixels.length;
for (var pixelIndex = 0; pixelIndex < pixelcount; pixelIndex += 4) {
// b の各ピクセルの RGBA 要素を取得する
var r = bPixels[pixelIndex + 0];
var g = bPixels[pixelIndex + 1];
var b = bPixels[pixelIndex + 2];
var a = bPixels[pixelIndex + 3];
// b のピクセルが緑の場合、それらを a のピクセルに置き換える
if (r == 0 && g == 255 && b == 0 && a == 255) {
bPixels[pixelIndex + 0] = aPixels[pixelIndex + 0];
bPixels[pixelIndex + 1] = aPixels[pixelIndex + 1];
bPixels[pixelIndex + 2] = aPixels[pixelIndex + 2];
bPixels[pixelIndex + 3] = aPixels[pixelIndex + 3];
}
}
return bImageData;
}
これはとても楽しい効果ですが、上記のレイ トレースの例と同様、現在のコンピューターでは処理に追いつくことができません。にもかかわらずこれらの例を紹介したのは、こうしたピクセル操作は SVG には不可能、ということを説明したかったからです。この点が、この 2 つのテクノロジを "差別化する要素" となります。一方はピクセルを操作し、もう一方はモデルを操作しているということです。
その他のシンプルなベクター グラフィックスからリアルな画像を作成するにしても、ビデオのグリーン スクリーン効果を作成するにしても、こうした画像シナリオの大部分は、端的に言えばまだ現状の Web 開発の主流とはなっていません。ただし、これらが十分に機能するシナリオも存在しないわけではありません (フィルターを適用して画像の赤目を除去するなど)。ピクセル操作のシナリオは、Canvas のシナリオとして分布図の左端に置きましょう。
混合型/中間的シナリオ
最も興味深い使用方法は、どちらか一方だけに片寄らないものです。これについて、チャート/グラフ/マップ作成シナリオと 2 次元ゲーム シナリオという重要な 2 つのシナリオを使って説明していきましょう。
チャートとグラフにはベクター グラフィックスが必要ですが、Canvas と SVG のどちらも使用できます。ただし一般的には、備わっている機能の特性という点で SVG の方が適切な選択肢となります。
SVG によるチャート/グラフ/マップ作成シナリオ
Web におけるチャートとグラフの種類には、主に以下のようなものがあります。
- インタラクティブな組織図/フローチャート
- インタラクティブ マップ - 経路検索
- 建物のフロア図
- 工学技術概略図
- 旅客機やイベント会場の座席表
- 一般的なデータ/財務チャート (棒グラフ、折れ線グラフ、散布図、ドーナツ チャート)
これらのすべてについて SVG テクノロジが適切である理由は以下のとおりです。
- XML を SVG に変換することで、既存データから簡単にチャートを生成可能
- ツール (Inkscape、Adobe Illustrator、Microsoft Visio、その他さまざまな CAD プログラム) を使って静的バージョンを簡単にエクスポート可能
- 正確なユーザー対話操作の実現が必要とされる
- サードパーティのコンテンツ プロバイダーが CSS スタイル設定を使用して Web 作成者向けのカスタマイズを行うことが可能
- アクセシビリティの実現が必要とされる
より詳しく説明するために、アメリカの地図上で州を選択するというシナリオを使って考えてみましょう。
上記のアラスカ州の詳細マップは、パブリック ドメインとして Wikimedia Commons ( 英語 ) に公開されています。
SVG では、アラスカ州を 1 つの <path> 要素と、その "d" 属性内の約 162,500 文字の幾何データで表現します。
<path id="AK" fill="#cdc3cc" d="M 777.5514,1536.1543 C 776.4904,1535.0933
776.7795,1530.0041 777.9416,1529.2859 C 781.3258,1527.1943 787.2657,1532.4522
784.8317,1535.3849 …" />
一方 Canvas では、この図形を以下のような一連の JavaScript 呼び出しによって作成できます。
function drawAlaska() {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(777.5514, 1536.1543);
ctx.bezierCurveTo(776.4904, 1535.0933, 776.7795, 1530.0041, 777.9416, 1529.2859);
ctx.bezierCurveTo(781.3258, 1527.1943, 787.2657, 1532.4522, 784.8317,1535.3849);
//
// 残り 2,875 件のパス描画命令
//
ctx.bezierCurveTo(1689.8261, 12.13753, 1689.1395, 12.17333, 1685.8848, 10.52683);
ctx.closePath();
ctx.fillStyle = "#cdc3cc";
ctx.fill();
}
実際には、複雑なアラスカ州のマップを描画するのに 2,878 件のパス描画命令 (moveTo、lineTo、および bezierCurveTo) が必要です。もちろんこのマップの解像度を下げることも可能です。ワイオミング州やコロラド州なら単純なので、コード行をかなり減らせますね。
SVG マップベースのアプリケーションは通常、マウスオーバー効果、選択、タブを使ったアイテム間の移動、拡大縮小などの対話エクスペリエンスをサポートします。これらの操作に必要なものは、SVG の使用時におけるライトウェイト HTML の概念だけです。たとえば以下のようなマウス イベントの処理や、
<path id="AK" fill="#cdc3cc" onmousedown="window.alert('Alaska');" d="M 777.5514,1536.1543 …" />
CSS を使ったマウスオーバー時のハイライト効果の作成が挙げられます。
path#AK:hover { fill: yellow; }
このようなインタラクティブ マップの例に、2004 年欧州議会選挙のドイツでの選挙結果を視覚化した Test Drive のデモ、Atlas zur Europawahl 2004 in Deutschland があります。
Canvas の場合は、こうした効果のいずれを作成する場合でも、イベント オブジェクトのマウス座標を使用して、ヒット検出のためのコードを記述する必要があります。また、図形のコンテキストは保持できません。isPointOnPath() API を使うこともできますが、適用されるのは最後に作成されたパスだけです。
コードはグラフ作成ライブラリの形式で利用できます。これによってグラフ上の特定のヒットを検出し、ピクセル データを使用してヒットとマウスオーバーを特定します。これでも問題ありませんが、SVG でもグラフ作成ライブラリを使えるので、これに SVG の機能を組み合わせればさらに高いパフォーマンスを実現できます。
Canvas によるチャート/グラフ作成シナリオ
Canvas はチャート作成とグラフ作成のシナリオのために独自の場所を設定します。そのためのコンテキストを設定するには、まず SVG と Canvas のパフォーマンス特性を検証する必要があります。
外部要因によってテクノロジの選択が余儀なくされる場合があります。ほぼ 1 つしかないその外部要因とは、機能の独立性です。SVG と Canvas の間には主に 2 つの差別化要因があります。
テクノロジの選択にあたっては、開発者の知識、スキル セット、既存の資産が大きな影響力を持ちます。たとえばゲーム開発の場合に、開発者が低レベルのグラフィックス API に習熟していて Web テクノロジの知識に乏しい場合、選択されるのは Canvas になることが多いでしょう (詳細については後述)。ゲームを移植する場合、サードパーティの実装を Canvas に移行するためのさまざまなツールが提供されています。
ミリ秒以下のレベルのパフォーマンスが求められる場合は 2 つのテクノロジのパフォーマンス特性を比較する必要があります。これは、一般に効率性が高いと考えられている Canvas を選択することが必ずしも望ましいということではありません。ただし、大量のデータをピクセル レベルで描画するアプリケーションの場合には、今のところ Canvas が圧倒的に有利な選択となります。
以下のような天気図は広いサーフェイス領域を必要としませんが、画面上のオブジェクトはかなりの数になります。Canvas であれば、DOM を更新するロスを発生させることなく高速に描画できます。
上記の画像は、ドット部分に SVG の circle または ellipse 要素を使うことですべて作成することはできますが、数千にもなる大量の要素を DOM にロードするために時間がかかりすぎてしまいます。ピクセルや画像が大量に表示される場合に使用すべきテクノロジは Canvas である、と覚えておくことができます。これは天文学、生物細胞の移動、音声変調の表示などでも同様です。この場合、データ視覚化のスピードを制限するものは、CPU の速度、Canvas 実装の速度、JavaScript 実装の速度です。
2 次元ゲーム
カジュアル ゲームは理解が最も複雑になるシナリオです。いくつかのポイントを以下に示します。
- ゲーム ライブラリでは低レベルのグラフィックス API を使用している
- ゲーム業界における開発者のスキルセットはこれらの低レベル API を志向している
- 多くのゲームが画像またはスプライトを多用している
- Adobe などのベンダー各社が Canvas エクスポートのサポートを開始している
- カジュアル ゲームの場合、高度なヒット テストが必要ないことが多い
- カジュアル ゲームの場合、"オブジェクト" がそれほど多く使用されない
たとえば有名な物理エンジンなどのゲーム ライブラリの場合、グラフィックス モデルは独立していて、グラフィックスは実装の詳細部分となります。エンジンには境界、速度、サイズ、位置などのグラフ作成ジオメトリが提供され、その後エンジンが速度、衝突、および位置で応答します。グラフィックスは、画面上の計算済みシーンの取得だけに使用されます。
グラフィックスがゲームのロジックから独立しているという概念は、SVG と Canvas の説明を目的として同じ作者によって開発された 2 つのゲーム SVG-oids と canvas-pinball で確認できます。
ゲームと表示ロジックは異なるものの、2 つは同じ物理エンジンを使って、ゲーム要素の位置、衝突、速度などの物理的処理を追跡しています。結果的には、一方が SVG を使ってゲーム要素を描画 (移動) し、もう一方が Canvas を使ってゲーム要素を再描画しているということになります。
現在 HTML5 向けに開発されている 2 次元のカジュアル ゲームの大部分が Canvas を使っているため、このシナリオについては、分岐点から Canvas 寄りに置きましょう。
混合型のシナリオ
カジュアル ゲームでは、混合型のシナリオを利用して、この 2 つのテクノロジの最も優れた点を活用することも可能です。SVG ジオメトリの不透明レイヤーの大部分を使って要素を配置すれば、ヒット検出とユーザー対話操作を簡単に実現できます。一方、対応する画像をよりすばやく配置してリアルタイムのアニメーションを提供するには、下位の Canvas を使います。
カジュアル ゲーム業界の外側では、混合型を採用することのメリットを示すさまざまな実例が登場しています。強烈なビジュアルを持つ動的なグラフィックスとアニメーション (Canvas) が必要で、同時に高度なユーザー対話操作 (SVG) も求められるようなシナリオでは、2 つのテクノロジの両方を使うことができ、また使う必要があります。この例は、マイクロソフトの The Beauty of the Web サイトにも参加しているパートナーが提供する、Brain Power に見ることができます。この Brain Power サイトを始めとする The Beauty of the Web サイトのさまざまなコンテンツには、この微妙なバランスがうまく保たれています。
このサイトでは、ユーザー対話操作と脳のグラフィックの各部の表示に、SVG の高レベルなジオメトリを使用しています。
<polygon id="SensoryCortex" points="253,80,266,93,…" style="fill: rgba(0,0,0,0)" />
一方、リアルタイムのアニメーションと特殊効果には Canvas が使用されています。
<canvas id="cnvDisplay" width="1920" height="1099" style="position:absolute;" />
まとめ
最新の各種ブラウザーが対応する既存のベクター グラフィック テクノロジを分析することで、標準の Web テクノロジを使って新しいインタラクティブなシナリオがさまざまな形で実現されることがわかりました。
Web は、より洗練されたグラフィックスの実現を目指してこれからも進化していくでしょう。ここでは、特定のシナリオでこれらのテクノロジをいかに適用するかということについて、1 つの考え方を説明しました。結論としては、HTML5 を基盤に Web の美しいグラフィックを実現するためには、Canvas と SVG の両方が欠かせないということです。
最新の HTML5 テクノロジを皆様がどのようにサイトに取り入れているか、ぜひお聞かせください。その際は URL を記載いただくほか、サイトが IE9 で動作するよう、HTML5 の DOCTYPE 宣言である <!DOCTYPE html> を記述し、SVG または Canvas サポートの判定にブラウザー検出ではなく機能検出を使用してください。
—Patrick Dengler、シニア プログラム マネージャー、Internet Explorer