【JavaScript入門】パスの描画

 ブラウザ Canvas の「パス描画」は、複数の点を結んで図形を作り、その 内側を塗りつぶす輪郭に沿って線を引く ベクター形式の描画手法です。ラスター形式(1 px 単位で色を塗る)に比べ、拡大・回転しても劣化せず、複雑な形を少ない命令で表現できます。ここではパスを組み立てるメソッドと、塗り・線・クリップの 3 大レンダリング操作を段階的に学びます。

1.パスを構築するメソッド

メソッド役割
beginPath()既存パスをリセットし、新しいパスの定義を開始する。
moveTo(x, y)始点を設定し、サブパスを開始する。
lineTo(x, y)指定座標へ直線パスを追加する。
closePath()始点と終点を結びパスを閉じる。

1.1. サブパスと複数図形

 moveTo() を使えば 1 つの beginPath() で複数図形を定義できます。サブパスごとに moveTolineTo… を繰り返し、最後にまとめて塗り / 線を適用すると高速です。

1.2. 開いたパスと閉じたパス

 closePath() を呼ばないと「開いた図形」になります。塗りつぶす場合は自動的に線を結んで面を作りますが、線描画時は結ばれません。

1.3. 曲線パス(発展)

 Bezier 曲線 quadraticCurveTo / bezierCurveTo や扇形の arc などでスムーズなカーブも追加できます。ここでは概要に留め、詳細は次章で扱います。

2.パスを描画・加工するメソッド

メソッド役割
fill()パス内部を現在の fillStyle で塗る。
stroke()パス輪郭を strokeStylelineWidth で描く。
clip()現在のパスを「切り抜きマスク」にする(以降の描画を制限)

2.1. 塗り (fill)

 パスが閉じているかどうかに関わらず、内部を塗ります。非ゼロ/偶数–奇数ルールも指定可能ですが既定では非ゼロです。

2.2. 線 (stroke)

 lineWidth, lineCap, lineJoin を組み合わせてデザインを調整します。線幅はパス中心基準で内外に等しく広がる点に注意。

2.3. クリップ (clip)

 clip() 実行後の描画は、パスの内側だけに現れます。save() で状態を退避し、不要になったら restore() で元に戻すのが定石です。

2.4. 高速化のヒント

  1. 設定変更 (fillStyle, strokeStyle など) はバッチごとにまとめる。
  2. 一連の図形を 1 回の beginPath() → 描画で完結させる。
  3. アニメーション時は clearRect または差分描画で最小限の更新範囲に抑える。

3.パスの描画:サンプルプログラム

ファイル名: path_demo.html

PNGファイルのダウンロードは、ここ「checker.png」からできます。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>🖌️ Canvas Path Demo</title>
<style>
  body{font-family:"Segoe UI",sans-serif;background:#f4f9ff;text-align:center;margin:2rem}
  h1 {font-size:1.8rem;margin-bottom:1rem}
  canvas{border:1px solid #666;border-radius:6px;background:url(./checker.png)}
  #msg{margin-top:.8rem;color:#004d40;font-weight:bold}
</style>
</head>
<body>
  <h1>🖌️ パスの描画デモ</h1>
  <canvas id="stage" width="420" height="260"></canvas>
  <p id="msg">パスを使って三角形を描きました!</p>

<script>
(() => {
  const cvs = document.getElementById('stage');
  const ctx = cvs.getContext('2d');

  /* 共通スタイル */
  ctx.fillStyle   = '#ffb3c1';     // 塗り色
  ctx.strokeStyle = '#3559e0';     // 線色
  ctx.lineWidth   = 10;
  ctx.lineCap     = 'round';
  ctx.lineJoin    = 'round';

  /* パス開始 */
  ctx.beginPath();

  /* 左の三角(閉じない)*/
  ctx.moveTo(70,50);
  ctx.lineTo(30,210);
  ctx.lineTo(150,210);

  /* 右の三角(閉じる)*/
  ctx.moveTo(260,50);
  ctx.lineTo(180,210);
  ctx.lineTo(340,210);
  ctx.closePath();            // 右のみ閉じる

  ctx.fill();                 // 両方塗りつぶし
  ctx.stroke();               // 輪郭を描画
})();
</script>
</body>
</html>

ブラウザの出力例

  • ピンク色で塗りつぶされた大小 2 つの三角形
  • 左は頂点間の上辺が描かれず、線は 2 辺のみ
  • 右は 3 辺すべて輪郭があり、頂点が丸く仕上がる
  • 下部に “パスを使って三角形を描きました!” と日本語メッセージ

プログラム解説

内容解説
16–18strokeStyle, lineWidth, lineCap, lineJoin線端と角を丸くすることで見栄えを向上
23beginPath()既存パスの消去+新規定義開始
26–28左三角の 3 頂点を moveTo/lineTo で指定closePath を呼ばないため開いた図形
31–34右三角を定義し closePath() で頂点結合ここでのみパスを閉じる。
36–37fill()stroke() の順塗りを先に行うと線が最前面に来て見やすい。

4.クリップ領域

4.1. クリップ領域の概要

 クリップ領域を設定すると、その領域内だけに描画が制限されます。領域外への描画はマスクされ、見えなくなります。

4.2. 主なメソッド

メソッド説明
ctx.save()現在の描画状態(変形行列・クリップ領域・スタイルなど)をスタックに保存
ctx.clip()直前に定義したパスをクリップ領域として設定
ctx.restore()スタックから保存した描画状態を復元し、クリップ領域も元に戻す。

5.クリップ領域:サンプルプログラム

ファイル名: clip_region.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>🎯 clip_region.html</title>
  <style>
    body { font-family:"Segoe UI",sans-serif; background:#f0f8ff; margin:2rem }
    h1   { text-align:center; font-size:1.8rem; margin-bottom:1rem }
    canvas { display:block; margin:0 auto; background:#fff; border:1px solid #ccc }
  </style>
</head>
<body>
  <h1>🎯 クリップ領域の利用</h1>
  <canvas id="canvas" width="400" height="300"></canvas>
  <script>
    const canvas = document.getElementById("canvas");
    const ctx    = canvas.getContext("2d");

    // 背景を描画
    ctx.fillStyle = "#eee";
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // クリップ前の状態を保存
    ctx.save();

    // クリップ領域を定義
    ctx.beginPath();
    ctx.rect(100, 50, 200, 150);
    ctx.clip();

    // クリップ領域内に円を描画
    ctx.fillStyle = "steelblue";
    ctx.beginPath();
    ctx.arc(200, 125, 100, 0, Math.PI * 2);
    ctx.fill();

    // 状態を復元しクリップを解除
    ctx.restore();

    // クリップ領域を視覚化する枠を描画
    ctx.strokeStyle = "tomato";
    ctx.lineWidth   = 3;
    ctx.strokeRect(100, 50, 200, 150);
  </script>
</body>
</html>

ブラウザの出力例

 キャンバス中央にある赤枠(100×50 〜 300×200 の矩形)内にだけ青い円が描画され、それ以外は背景色だけが見える。

プログラム解説

  • ctx.save():現在のクリップ領域を含む描画状態を保存。
  • ctx.rect()ctx.clip():矩形パスをクリップ領域として設定し、以降の描画をその内側だけに制限。
  • ctx.arc()ctx.fill():円を描画するが、クリップ領域外は自動的にマスクされる。
  • ctx.restore():保存しておいた描画状態を復元し、クリップ領域を解除。
  • ctx.strokeRect():クリップ領域を赤い枠で可視化。

 これで、.save().clip().restore() を組み合わせて、必要な領域だけに描画を行う方法が理解できました。

まとめ

  • パスは beginPath → 点列定義 → fill/stroke の 3 ステップ。
  • 1 つの beginPath() に複数サブパスを入れると、描画回数を減らして性能向上。
  • clip() を組み合わせればマスク表現も可能。

 これらを応用すると、チャートライブラリの折れ線やゲームのキャラクター当たり判定など、あらゆるベクター描画に展開できます。まずは図形を自由に組み合わせ、パス操作に慣れてみましょう。