タイトル: Canvas API
SEOタイトル: HTML5 Canvas API 入門(描画 / アニメーション / Three.js / PixiJS への発展)
この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li>Canvas API は <strong><canvas> 要素 + JavaScript</strong> でブラウザにビットマップ描画する API</li><li><code>canvas.getContext("2d")</code> で 2D コンテキスト、<code>"webgl"</code> で 3D(OpenGL ES2 ラッパ)</li><li>基本図形: <code>fillRect</code> / <code>strokeRect</code> / <code>arc</code>(円) / <code>beginPath + moveTo + lineTo + stroke</code>(線)</li><li>画像・テキスト: <code>drawImage(img, x, y)</code> / <code>fillText(text, x, y)</code></li><li>アニメーションは <strong>requestAnimationFrame</strong>。複雑になったら <strong>PixiJS</strong>(2D)・<strong>Three.js</strong>(3D)を使う</li></ul></td></tr></table>
<h2>Canvas API とは</h2>
<p>HTML5 で導入された <code><canvas></code> 要素は、JavaScript からピクセル単位で描画できる<strong>ビットマップキャンバス</strong>です。ゲーム、グラフ、画像処理、データビジュアライゼーションなど幅広く使われます。</p>
<h2>最小サンプル: 四角形を描く</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><!DOCTYPE html>
<html>
<body>
<canvas id="c" width="500" height="300" style="border:1px solid #ccc"></canvas>
<script>
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
// 塗りつぶし四角形
ctx.fillStyle = '#3498db';
ctx.fillRect(20, 20, 100, 60);
// 枠線のみの四角形
ctx.strokeStyle = '#e74c3c';
ctx.lineWidth = 3;
ctx.strokeRect(150, 20, 100, 60);
// クリア
// ctx.clearRect(0, 0, canvas.width, canvas.height);
</script>
</body>
</html></code></pre>
<p><strong>width / height は CSS でなく属性で指定</strong>するのが鉄則。CSS で指定すると拡大表示になりピクセルがぼやけます。</p>
<h2>基本図形の描画</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// 直線
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(200, 100);
ctx.lineTo(50, 150);
ctx.closePath(); // パスを閉じる
ctx.stroke(); // 線として描画
// ctx.fill(); // 塗りつぶし
// 円 / 円弧
ctx.beginPath();
ctx.arc(300, 100, 50, 0, Math.PI * 2); // x, y, r, 開始角, 終了角(ラジアン)
ctx.fillStyle = '#2ecc71';
ctx.fill();
// 半円
ctx.beginPath();
ctx.arc(400, 100, 50, 0, Math.PI);
ctx.stroke();
// 二次ベジェ曲線
ctx.beginPath();
ctx.moveTo(50, 200);
ctx.quadraticCurveTo(150, 100, 250, 200); // 制御点, 終点
ctx.stroke();</code></pre>
<h2>テキスト描画</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">ctx.font = 'bold 32px sans-serif';
ctx.fillStyle = '#2c3e50';
ctx.textAlign = 'center'; // start | end | left | right | center
ctx.textBaseline = 'middle'; // top | hanging | middle | alphabetic | ideographic | bottom
ctx.fillText('Hello Canvas', 250, 50);
// 枠線のみ
ctx.strokeStyle = '#e74c3c';
ctx.lineWidth = 2;
ctx.strokeText('Outline', 250, 100);
// 文字幅を測る
const m = ctx.measureText('Hello');
console.log(m.width); // pixel</code></pre>
<h2>画像の描画</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">const img = new Image();
img.src = '/path/to/photo.jpg';
img.onload = () => {
// 原寸
ctx.drawImage(img, 50, 50);
// サイズ指定
ctx.drawImage(img, 50, 50, 200, 150);
// 切り抜き + 配置(sx, sy, sw, sh, dx, dy, dw, dh)
ctx.drawImage(img, 0, 0, 100, 100, 300, 50, 200, 200);
};</code></pre>
<h2>変形(回転・拡大・移動)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">ctx.save(); // 状態を保存
ctx.translate(250, 150); // 原点を移動
ctx.rotate(Math.PI / 4); // 45 度回転
ctx.scale(1.5, 1.5); // 1.5 倍拡大
ctx.fillStyle = '#9b59b6';
ctx.fillRect(-50, -25, 100, 50); // 移動後の原点が中心
ctx.restore(); // 元の状態に戻す</code></pre>
<h2>アニメーション: requestAnimationFrame</h2>
<p>毎フレーム描き直すには <code>requestAnimationFrame</code> を使います。<code>setInterval</code> ではブラウザの再描画タイミングと同期しないのでカクつきます。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">let x = 0;
let vx = 2;
function tick() {
// 1. 画面クリア
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 2. 状態更新
x += vx;
if (x > canvas.width - 50 || x < 0) vx = -vx;
// 3. 描画
ctx.fillStyle = '#3498db';
ctx.fillRect(x, 100, 50, 50);
// 4. 次フレームを予約
requestAnimationFrame(tick);
}
tick();</code></pre>
<h2>マウス入力でお絵描き</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">let drawing = false;
canvas.addEventListener('mousedown', e => {
drawing = true;
ctx.beginPath();
ctx.moveTo(e.offsetX, e.offsetY);
});
canvas.addEventListener('mousemove', e => {
if (!drawing) return;
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
});
canvas.addEventListener('mouseup', () => drawing = false);</code></pre>
<h2>Canvas API でできることの限界</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>用途</th><th>素の Canvas</th><th>推奨ライブラリ</th></tr></thead>
<tbody>
<tr><td>簡単な図形・グラフ</td><td>OK</td><td>Chart.js</td></tr>
<tr><td>2D ゲーム(スプライト多数)</td><td>遅い</td><td><strong>PixiJS</strong>(WebGL 加速)</td></tr>
<tr><td>3D 描画</td><td>不可(getContext("webgl") 必要)</td><td><strong>Three.js</strong> / Babylon.js</td></tr>
<tr><td>SVG 的なベクタ</td><td>パスは描けるが座標管理が大変</td><td>SVG + D3.js</td></tr>
<tr><td>動画フィルタ</td><td>getImageData / putImageData で可能</td><td>WebGL + シェーダ</td></tr>
</tbody>
</table>
<h2>WebGL コンテキストへの拡張</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// 2D → 3D の入り口
const gl = canvas.getContext('webgl2');
if (!gl) {
alert('WebGL2 がサポートされていません');
}
// 実際は Three.js を使うのが現実的
// npm install three
import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, w/h, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ canvas });
const cube = new THREE.Mesh(
new THREE.BoxGeometry(),
new THREE.MeshNormalMaterial()
);
scene.add(cube);
camera.position.z = 3;
renderer.setAnimationLoop(() => {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
});</code></pre>
<h2>FAQ</h2>
<p><strong>Q: Canvas で描いた絵を画像保存したい</strong><br />
A: <code>canvas.toDataURL('image/png')</code> で base64 取得、<code>canvas.toBlob(cb)</code> で Blob 取得が可能です。</p>
<p><strong>Q: 高 DPI(Retina)でぼやける</strong><br />
A: <code>canvas.width = w * devicePixelRatio</code> にして <code>ctx.scale(devicePixelRatio, devicePixelRatio)</code>、CSS 側は <code>w</code> px に。</p>
<p><strong>Q: SVG とどちらが良い?</strong><br />
A: 拡大に強く要素単位でイベント取りたいなら SVG、ピクセル制御や多数オブジェクトのアニメは Canvas が高速です。</p>