はじめに
Webページで次のような図形を表示するには、どのような方法があるでしょうか。
- ボタンやカードのような四角形
- 円や線を組み合わせた図解
- 拡大・縮小できる地図
- 大量の点を表示するグラフ
- ブラウザ上で動くゲーム
代表的な方法として、次の3つがあります。
- CSS:HTML要素を装飾して表現する
- SVG:図形を要素として追加する
- Canvas:描画領域に図形を描き込む
どれも画面に図形を表示できますが、得意なことやプログラムからの操作方法が異なります。
本記事では、それぞれの特徴と使い分けを解説します。
Webページで図形を描く
まずは、今回比較する3つの方法を確認します。
円を書く例で見ていきます。
CSSで描く
<div class="circle"></div>
.circle {
width: 100px;
height: 100px;
background-color: tomato;
border-radius: 50%;
}
CSSの特徴
CSSの最大の特徴は、Webページの部品を作る仕組みをそのまま利用できることです。
CSSはWebページの部品や簡単な図形を表現する場合に向いています。
- ボタンやカード
- 円形のアイコン背景
- 進捗状況を表すバー
- 簡単な装飾やアニメーション
- 吹き出しやラベル
また、HTML要素なので、クリックイベントやキーボード操作など、通常のWebページと同じ仕組みを利用できます。
一方、複雑な図形を作ろうとすると、HTML要素やCSSの指定が増えてしまいます。
SVGで描く
<svg width="200" height="150" viewBox="0 0 200 150">
<circle cx="100" cy="75" r="40" fill="tomato" />
</svg>
SVGでは、図形を座標で表現するため、表示領域の大きさを変更しても、元の図形の形状を保ったまま拡大・縮小できます。
SVGの特徴
SVGはHTML要素と同じようにDOMの一部として扱われるため、JavaScriptから操作できます。
SVGは個々の図形を識別したり、操作したりする必要がある場合に向いています。
- アイコンやロゴ
- フローチャート
- 図解や模式図
- グラフ
- クリックや選択が必要な図形
ただし、大量の図形を個別の要素として生成すると、DOMの管理や更新に負荷がかかる場合があります。
Canvasで描く
<canvas id="myCanvas" width="200" height="150">
</canvas>
<script>
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
ctx.fillStyle = "tomato";
ctx.beginPath();
ctx.arc(100, 75, 40, 0, Math.PI * 2);
ctx.fill();
</script>
Canvasの特徴
Canvasでは描画した円が独立したDOM要素として保存されるわけではありません。
Canvasに円を描いた後、JavaScriptから「2番目に描いた円の色を変更する」といった操作を直接行うことはできません。
個々の図形を操作したい場合は、座標や色などの情報をJavaScript側で管理し、必要に応じて描画し直します。
一見すると不便ですが、この仕組みには利点もあります。
大量の図形を描く場合、図形ごとにDOM要素を生成する必要がありません。
また、描画領域全体を更新する処理も実装できます。
Canvasは描画領域全体を効率よく更新したい場合に向いています。
- ブラウザ上のゲーム
- 動的なグラフ
- 画像処理
- 大量の点や線の描画
- アニメーション
- 手書き入力やペイントツール
ただし、Canvasだから必ず高速というわけではありません。描画する図形の数や複雑さ、更新方法によって性能は変わります。
比較
CSS・SVG・Canvasの基本的な仕組みを確認しました。
ここからは、実際の開発で気になるポイントを比較します。
図形をクリックしたときの違い
画面に円を表示し、クリックすると色が変わる機能を考えます。
CSS、SVG、Canvasでは、それぞれ実装方法が異なります。
CSSの場合
HTML要素にクリックイベントを設定します。
<div id="circle" class="circle"></div>
<script>
document.getElementById("circle")
.addEventListener("click", event => {
event.currentTarget.style.backgroundColor = "royalblue";
});
</script>
SVGの場合
円そのものにイベントを設定できます。
<svg width="200" height="150" viewBox="0 0 200 150">
<circle id="target" cx="100" cy="75" r="40" fill="tomato" />
</svg>
<script>
const circle = document.getElementById("target");
circle.addEventListener("click", () => {
circle.setAttribute("fill", "royalblue");
});
</script>
Canvasの場合
Canvasでは、クリックされた座標を取得し、その座標に図形が存在するかを判定する必要があります。
単純な円なら、次のような計算で判定できます。
const dx = mouseX - circleX;
const dy = mouseY - circleY;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= radius) {
// 円がクリックされた
}
mouseX と mouseY はクリック位置、circleX と circleY は円の中心座標、radius は円の半径です。
クリック位置と円の中心との距離が半径以下なら、円の内側をクリックしたと判定できます。
実際には、Canvas上の表示座標と内部座標の変換、複数図形の重なり、拡大・縮小なども考慮する必要があります。
比較結果
| 項目 | CSS | SVG | Canvas |
|---|---|---|---|
| クリック対象 | HTML要素 | 図形要素 | 描画領域 |
| 図形の特定 | 要素を取得 | 要素を取得 | 座標などから判定 |
| 色の変更 | CSSを変更 | 属性を変更 | 再描画する |
| 実装の分かりやすさ | 簡単 | 簡単 | 自前の管理が必要 |
図形を個別にクリックしたい場合は、CSSやSVGが扱いやすいです。
拡大・縮小したときの違い
次に、図形を拡大・縮小した場合を考えます。
表示サイズを2倍する例です。
CSSの場合
CSSでは、transform: scale() などを使って要素を拡大・縮小できます。
.circle {
width: 100px;
height: 100px;
border-radius: 50%;
background-color: tomato;
transform: scale(2);
}
CSSによる拡大・縮小は簡単ですが、複雑な図形を拡大しながら、その内部の座標や線の太さなども管理したい場合には、別途設計が必要になります。
SVGの場合
SVGでは、viewBox を利用して内部座標と表示領域の関係を指定できます。
<svg width="200" height="200" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="30" fill="tomato" />
</svg>
SVGはベクター形式なので、拡大・縮小しても図形の輪郭を鮮明に保ちやすいという特徴があります。
Canvasの場合
Canvasでは、描画時に座標変換を適用できます。
ctx.scale(2, 2);
ctx.fillStyle = "tomato";
ctx.beginPath();
ctx.arc(50, 50, 30, 0, Math.PI * 2);
ctx.fill();
内部の描画解像度を変えずに表示だけを大きくすると、輪郭が粗くなることがあります。
高解像度で描き直すことで画質を改善できますが、メモリや描画処理とのバランスを考える必要があります。
比較結果
| 項目 | CSS | SVG | Canvas |
|---|---|---|---|
| 拡大・縮小 | 得意 | 得意 | 可能 |
| 輪郭の鮮明さ | 通常は良好 | 良好 | 解像度に依存 |
| 再描画の管理 | ブラウザが担当 | ブラウザが担当 | 必要に応じて自前で実装 |
SVGは拡大・縮小してもベクターとして描画できる一方、Canvasでは描画解像度の管理が重要になります。
大量の図形を描画する場合
大量の図形を描画するケースを考えます。
10万個の点を表示する例です。
CSSの場合
要素数が増えると、DOMの管理やレイアウト、スタイル計算などの負荷が問題になる場合があります。
CSS自体が遅いというよりも、大量のHTML要素を管理する構成が負担になりやすいと考えるとよいでしょう。
SVGの場合
ただし、図形の数が増えるほどDOMが大きくなり、生成・更新・イベント管理などの負荷が増えます。
すべての図形を常に表示する必要があるのか、画面に表示される範囲だけを描画するのか、といった工夫が重要です。
Canvasの場合
図形ごとにDOM要素を生成しなくてよいため、大量の単純な図形を描画する用途では有力な選択肢になります。
ただし、10万個の図形を毎フレーム描き直すような処理では、Canvasでも負荷が高くなります。
描画範囲の限定、不要な再描画の削減、描画処理の最適化などが重要です。
比較結果
| 項目 | CSS | SVG | Canvas |
|---|---|---|---|
| 大量の図形 | DOM管理に注意 | DOM管理に注意 | 有力な選択肢 |
| 個別の操作 | 得意 | 得意 | 自前の管理が必要 |
| 描画更新 | 要素の状態を変更 | 要素の状態を変更 | 再描画する設計が多い |
| 性能の決まり方 | 要素数やレイアウトなど | 要素数や描画内容など | 描画回数や描画内容など |
図形の種類、更新頻度、クリック判定、ブラウザや端末の性能などによって結果は変わります。
どの方式を選ぶべきか
ここまでの内容を整理すると、次のようになります。
| やりたいこと | おすすめの方式 | 理由 |
|---|---|---|
| ボタンやカードを作る | CSS | HTML要素と組み合わせやすい |
| 円や四角形を装飾として表示する | CSS | 簡単に実装できる |
| アイコンやロゴを作る | SVG | 拡大しても鮮明 |
| フローチャートを作る | SVG | 図形ごとの管理がしやすい |
| クリック可能な地図を作る | SVG | 図形ごとのイベントを設定しやすい |
| 大量の点を表示する | Canvas | DOM要素を大量に生成せず描画できる |
| ペイントツールを作る | Canvas | 描画領域全体を更新する用途に向く |
| ブラウザゲームを作る | Canvas | ゲームの描画処理に向く |
これはあくまで目安です。
すべてを1つの方法で実装する必要はありません。
- 背景はCanvasで描画する
- 操作用の図形はSVGで描画する
- 検索条件やボタンなどはCSSで作る
このように、複数の方式を組み合わせる設計も考えられます。
実際の開発では、描画性能だけでなく、操作性、保守性、実装コストも含めて判断することが大切です。
おわりに
今回は、Webページで図形を描画する方法として、CSS・SVG・Canvasを比較しました。
それぞれの特徴を簡単に振り返ります。
- CSS:Webページの部品や簡単な図形を作る
- SVG:図形を個別に管理し、拡大・縮小や操作を行う
- Canvas:描画領域に図形を描き込み、動的な描画を行う
大切なのは、どれか一つが常に優れているわけではないということです。
- 何を表示したいのか
- 個々の図形を操作する必要があるのか
- どれくらいのデータ量を扱うのか
この3点を整理すると、描画方式を選びやすくなります。
Webページで図形を扱う際には、ぜひ今回紹介した特徴を参考にしてみてください。
おまけ
WebGL・WebGPUという選択肢
CSS・SVG・Canvasの3つを比較してきました。
しかし、大量の図形や3Dデータを扱う場合には、もう一つ知っておきたい技術があります。
それがWebGLとWebGPUです。
WebGLとは
WebGLはWebブラウザ上でGPUを利用してグラフィックスを描画するためのAPIです。
WebGLを利用すると、次のようなものをブラウザ上で描画できます。
- 3Dモデル
- 地形や建物の3D表示
- 大量の点群データ
- 3Dゲーム
Canvas 2Dでも大量の点を描画できますが、3D表示やGPUを活用した高度な描画ではWebGLが有力な選択肢になります。
WebGPUとは
WebGPUは、GPUを利用するための比較的新しいWeb APIです。
高度なグラフィックス描画だけでなく、GPUによる計算処理も視野に入れた設計になっています。
どこから検討すべきか
最初からWebGLやWebGPUを使うのではなく、必要な機能やデータ量に応じて段階的に検討するとよいでしょう。
- 通常のWebページの部品ならCSS
- 個別に操作する図形ならSVG
- 動的な描画や大量の図形ならCanvas
- 高度な3D描画やGPU活用が必要ならWebGL・WebGPU