この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:6
ページ更新者:guest
更新日時:2026-06-11 07:07:02

タイトル: 直線・曲線(パスでの描画)
SEOタイトル: Canvas Path 直線・曲線描画完全ガイド

この記事の要点
  • Canvas のパス描画は beginPath()moveTo() → 各種描画 → stroke() / fill() の流れ
  • 直線: lineTo(x, y)。複数つなげると折れ線
  • 曲線: quadraticCurveTo (制御点 1)、bezierCurveTo (制御点 2)、arc / arcTo (円弧)、ellipse (楕円)
  • Path2D オブジェクトでパスを使い回せる。SVG パス文字列を直接渡せる
  • 線スタイル: lineWidth, lineCap, lineJoin, setLineDash。塗り潰しは nonzero / evenodd

パス描画の基本フロー

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

// ★ 基本パターン
ctx.beginPath();              // 1. パスを開始
ctx.moveTo(10, 10);           // 2. 始点へ
ctx.lineTo(100, 10);          // 3. 各種描画
ctx.lineTo(100, 100);
ctx.closePath();              // 4. 始点に戻す (任意)
ctx.strokeStyle = '#000';
ctx.stroke();                 // 5. 線を描く
// または
ctx.fillStyle = '#3498db';
ctx.fill();                   // 5'. 塗りつぶす

beginPath() を毎回呼ぶのが鉄則。呼ばないと前の stroke() 対象が累積し、線幅変更が過去パスにも適用される事故が起きます。

直線とポリライン

// 一本の直線
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(200, 100);
ctx.stroke();

// 三角形
ctx.beginPath();
ctx.moveTo(100, 50);
ctx.lineTo(200, 200);
ctx.lineTo(50, 200);
ctx.closePath();              // 3 辺目を自動で描いて閉じる
ctx.stroke();

// 折れ線 (ポリライン)
ctx.beginPath();
ctx.moveTo(0, 100);
[50, 80, 120, 200, 250].forEach((y, i) => {
    ctx.lineTo(i * 50, y);
});
ctx.stroke();

円弧 (arc / arcTo)

// arc(x, y, radius, startAngle, endAngle, counterclockwise)
ctx.beginPath();
ctx.arc(100, 100, 50, 0, Math.PI * 2);    // 完全な円
ctx.stroke();

// 半円 (右半分)
ctx.beginPath();
ctx.arc(100, 100, 50, -Math.PI / 2, Math.PI / 2);
ctx.stroke();

// 反時計回り
ctx.beginPath();
ctx.arc(100, 100, 50, 0, Math.PI, true);
ctx.stroke();

// arcTo(x1, y1, x2, y2, radius)
// 現在位置から (x1,y1) へ向かい、半径 radius の角丸を描いて (x2,y2) 方向へ
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.arcTo(150, 50, 150, 150, 30);   // 30 で角丸の四角
ctx.lineTo(150, 200);
ctx.stroke();

2 次ベジェ (quadraticCurveTo)

// quadraticCurveTo(cpx, cpy, x, y)
// 現在位置から (x, y) へ、(cpx, cpy) を制御点として 2 次ベジェ

ctx.beginPath();
ctx.moveTo(50, 200);
ctx.quadraticCurveTo(150, 50, 250, 200);   // 山型
ctx.stroke();

// 制御点を可視化
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(150, 50, 4, 0, Math.PI * 2);
ctx.fill();

3 次ベジェ (bezierCurveTo)

// bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
// 制御点 2 つで S 字なども描ける

ctx.beginPath();
ctx.moveTo(50, 200);
ctx.bezierCurveTo(
    100, 50,    // 制御点 1
    200, 350,   // 制御点 2
    250, 200,   // 終点
);
ctx.stroke();

楕円 (ellipse)

// ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle)
ctx.beginPath();
ctx.ellipse(150, 100, 80, 40, 0, 0, Math.PI * 2);
ctx.stroke();

// 45 度回転した楕円
ctx.beginPath();
ctx.ellipse(150, 100, 80, 40, Math.PI / 4, 0, Math.PI * 2);
ctx.stroke();

Path2D オブジェクト

Path2D はパスを再利用可能なオブジェクトとして持てる API。同じ図形を何度も描く場合に便利:

// パスを 1 度作成
const triangle = new Path2D();
triangle.moveTo(0, 0);
triangle.lineTo(50, 0);
triangle.lineTo(25, 40);
triangle.closePath();

// 何度でも使える
for (let i = 0; i < 5; i++) {
    ctx.save();
    ctx.translate(i * 60, 0);
    ctx.fillStyle = `hsl(${i * 60}, 70%, 50%)`;
    ctx.fill(triangle);
    ctx.restore();
}

// SVG パス文字列を直接渡せる ★
const star = new Path2D('M 50 0 L 60 35 L 95 35 L 67 55 L 78 90 L 50 70 L 22 90 L 33 55 L 5 35 L 40 35 Z');
ctx.fillStyle = 'gold';
ctx.fill(star);

線スタイル

ctx.lineWidth = 5;             // 太さ
ctx.strokeStyle = '#e74c3c';   // 色
ctx.lineCap = 'round';         // butt | round | square (端の形状)
ctx.lineJoin = 'round';        // miter | round | bevel (角の形状)
ctx.miterLimit = 10;           // miter のとがり制限

// 破線
ctx.setLineDash([10, 5]);      // 10 描画 / 5 空白
ctx.lineDashOffset = 0;
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(250, 50);
ctx.stroke();

// 1 点鎖線
ctx.setLineDash([20, 5, 2, 5]);

// 元に戻す
ctx.setLineDash([]);

塗り潰し規則 (fill rule)

// 自己交差する図形で内側判定が変わる
ctx.fill('nonzero');     // ★ デフォルト
ctx.fill('evenodd');     // SVG と同じ規則 (奇数回交差で内側)

// 例: 五芒星
ctx.beginPath();
for (let i = 0; i < 5; i++) {
    const a = -Math.PI / 2 + i * Math.PI * 4 / 5;  // 2 つ飛ばし
    const x = 100 + Math.cos(a) * 80;
    const y = 100 + Math.sin(a) * 80;
    i ? ctx.lineTo(x, y) : ctx.moveTo(x, y);
}
ctx.closePath();
ctx.fillStyle = 'gold';
ctx.fill('evenodd');     // 中央のくぼみが抜ける

パス操作系メソッドまとめ

メソッド概要
beginPath()パスをリセット
closePath()始点に戻す直線を引く
moveTo(x, y)ペン位置を移動 (描画なし)
lineTo(x, y)直線
quadraticCurveTo2 次ベジェ
bezierCurveTo3 次ベジェ
arc円弧 (中心 + 半径 + 角度)
arcTo2 接線の間の角丸円弧
ellipse楕円
rect(x, y, w, h)矩形パス
roundRect(x, y, w, h, r)角丸矩形 (Chrome 99+)
stroke() / fill()描画実行
clip()クリッピング領域に設定
isPointInPath当たり判定

SVG パスからの変換

SVG の d 属性のパス文字列はそのまま Path2D に渡せます:

// SVG 風のハート
const heart = new Path2D(`
    M 100 30
    C 100 10, 60 10, 60 50
    C 60 80, 100 100, 100 130
    C 100 100, 140 80, 140 50
    C 140 10, 100 10, 100 30
    Z
`);
ctx.fillStyle = '#e91e63';
ctx.fill(heart);

パフォーマンス Tips

  • Path2D を再利用: 毎フレーム同じパスを作り直さない
  • beginPath() を必ず: パスが累積するとパフォーマンス劣化
  • 整数座標を使う: 0.5 オフセットで半画素を回避すると線がシャープに
  • OffscreenCanvas + Worker: 重い描画は Worker に逃がす
  • requestAnimationFrame でループ (setInterval ではなく)

FAQ

Q: 線が滲んで見える
A: 整数座標に +0.5 オフセットすると 1px ラインが滲まなくなります。ctx.translate(0.5, 0.5) も可。

Q: クリックしたパスを判定したい
A: ctx.isPointInPath(path, x, y) / ctx.isPointInStroke(path, x, y)。Path2D 渡しが推奨。

Q: 矢印を描きたい
A: 線端に三角形を描く。角度は Math.atan2(dy, dx) で求めて rotate + 三角形 fill。