JavaScriptのCanvas APIで穴の開いた図形を描画する方法を解説します。

下準備

まずは下準備から。htmlの<body></body>内に以下のcanvas要素を配置します。

<canvas id="canvas"></canvas>

jsにてコンテキストを取得します。

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

画像ではキャンバスの範囲がわかりやすいようにcssにて背景色を設定してあります。

穴の開いた矩形を描画

穴の開いた図形を作るにはパスで描画する必要があります。まずは以下のコードで矩形を描画します。

ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(110, 10);
ctx.lineTo(110, 110);
ctx.lineTo(10, 110);
ctx.fill();
矩形を描画

次に、内側に逆回りにパスを描くことでくり抜くことができます。時計回りにパスを描いたので、内側の図形は反時計回りにパスを描きます。fill()の直前に以下のコードを追加します。

ctx.moveTo(20, 20);
ctx.lineTo(20, 100);
ctx.lineTo(100, 100);
ctx.lineTo(100, 20);
穴の開いた矩形

矩形の内側を矩形でくり抜くことができました。ここで重要なのは、外側と内側の図形のパスをfill()で同時に処理することです。例えば以下のように別々に描画するとくり抜くことができません。

ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(110, 10);
ctx.lineTo(110, 110);
ctx.lineTo(10, 110);
ctx.fill();

ctx.beginPath();
ctx.moveTo(20, 20);
ctx.lineTo(20, 100);
ctx.lineTo(100, 100);
ctx.lineTo(100, 20);
ctx.fill();
くり抜き失敗

また、rect()を使うと時計回りにパスが作られるため、以下のように短く書くこともできます。

ctx.beginPath();

ctx.rect(10, 10, 100, 100);

ctx.moveTo(20, 20);
ctx.lineTo(20, 100);
ctx.lineTo(100, 100);
ctx.lineTo(100, 20);

ctx.fill();
rect()を使い、短く書いたコード

穴の開いた円を描画

以下のコードでは穴の開いた円を作ることができます。

ctx.beginPath();
ctx.arc(60, 60, 50, 0, 2 * Math.PI);
ctx.arc(60, 60, 30, 0, 2 * Math.PI, true);
ctx.fill();
穴の開いた円

arc()メソッドの最後の引数を省略すると時計回り、trueを指定すると反時計回りにパスが作られるためそれぞれ違う値を指定します。

もちろん、矩形を円でくり抜いたり、他にもパスで柔軟に描いた図形でも同じことが実現できます。