HTML canvasで複数の円を描写したら意図しない変な図が表示された
HTMLのcanvas要素とJavaScriptを組み合わせてアニメーションの練習をしていました。
手始めに複数の円をばばっと描写してみようと思ってやったら全く意図していない挙動になって、初心者がすごい驚いた話です。
こんなコードを書きました。
// canvasを取得
const canvas = document.querySelector("canvas");
// canvasの高さを画面の高さに
canvas.height = window.innerHeight;
// 横幅を画面の横幅に
canvas.width = window.innerWidth;
// 2Dのオブジェクトとして取得
const c = canvas.getContext("2d");
// 複数の円を描写
// 書き始め地点を初期化
c.beginPath();
// ★4つの円を定義
c.arc(400, 400, 100, 0, Math.PI*2, false);
c.arc(400, 200, 100, 0, Math.PI*2, false);
c.arc(200, 200, 100, 0, Math.PI*2, false);
c.arc(200, 400, 100, 0, Math.PI*2, false);
// canvasに描写 -> これで4つの円が表示...????
c.stroke();
★のc.arc(...)メソッドのところでx, y座標をずらした4つの円を定義しました。
そして最後のstroke()メソッドで画面に描画します。
謎の「Z」の文字が・・・
ところが、この結果ブラウザに表示されたのは以下のような図でした。
いや、なんで!?
想定もしていなかったZの文字。だって書いたのは円だけなのに・・・。
しかもあまりにキレイで見事な Z だったので何かしらやんごとなき大いなる意志(?)を感じてしまい余計驚きました。
結果的から言うと、以下のように書き直して円だけを4つ描写できるようになりました。
<pre class="font-size:15 top-margin:15 bottom-margin:15 lang:js decode:true" title="canvas.js">// BEFORE (コメントアウトは削除)
c.beginPath();
c.arc(400, 400, 100, 0, Math.PI*2, false);
c.arc(400, 200, 100, 0, Math.PI*2, false);
c.arc(200, 200, 100, 0, Math.PI*2, false);
c.arc(200, 400, 100, 0, Math.PI*2, false);
c.stroke();
// AFTER
c.beginPath();
c.arc(400, 400, 100, 0, Math.PI*2, false);
c.stroke();
c.beginPath();
c.arc(400, 200, 100, 0, Math.PI*2, false);
c.stroke();
c.beginPath();
c.arc(200, 200, 100, 0, Math.PI*2, false);
c.stroke();
c.beginPath();
c.arc(200, 400, 100, 0, Math.PI*2, false);
c.stroke();
もちろんfor文を使えば短くできますが、敢えて繰り返して書いてます。
c.beginPath()メソッドで書き始め地点の初期化
↓
c.arc(...)メソッドで軌道を作る
↓
c.strokeメソッドで実際に描画
↓
c.beginPath()メソッドで書き始め地点の初期化
↓
.......
というふうに、書いては初期化書いては初期化という手順を踏む必要があります。
円の軌道終点から、次の円の軌道始点への間が線となって現れてしまったということでした。
ポイントは、"2D"であるということ
これは僕たちが生きている立体性のある3D空間の話ではなく「2D世界の話」ということを考えると、なぜこんな脈絡のない線が現れるのかがわかりました。
たとえば、以下のように●を始点として円を描きます。
一周して終点につきました。
3D世界に生きる人間なら「ふう、描き終わった」と言ってペンを空中に浮かせてしまうでしょう。
しかし2Dの世界では、平面がその世界の全てなのでペンはそのまま紙から離すことはできません。
では、その2D世界前提で以下破線で示した円を「連続して最短距離で」描きたい場合はどうすればいいでしょうか?
円を突っ切るしかないですね。
これはどこかで見覚えがあると思ったら、最初の画像のこの部分。
こう考えると、コードを実行したコンピュータがどういう順番、軌跡で描画したかが見えるのではないでしょうか。
よく見たら最初の方で「const c = canvas.getContext("2d");」って宣言していますね。2Dだぞ、と。
円に限らずcanvasとJavaScriptで図形描写をするときは「目には見えないけど描写の終点位置が記憶されている」ということを意識する必要があります。