demo_canvas の設計

設計目標

demo_canvas は、できるだけ少ないブラウザ用コード(キャンバス 1 つ、セレクタ 1 つ、アニメーションループ 1 つ)で、実際のページ上に Canvas バックエンドを示します。ターミナルデモのシーンを再利用しているので、2 つの出力を直接比べられます。

制約

  • プログラムは自分で作ったのではないブラウザのページで動きます。必要な要素は index.html に含まれていなければなりません。
  • 使えるのはライブラリパッケージと rabbita の公開 API だけです。

数学的背景

アニメーションフレームからの時刻

requestAnimationFrame はページ開始からのミリ秒単位のタイムスタンプ τ\tau を渡します。トーラスのシーンは角度 θ=0.0006 τ\theta = 0.0006\,\tau だけ回るので、yy まわりの 1 回転には 2π/0.0006≈10.52\pi / 0.0006 \approx 10.5 秒かかり、ディスプレイのリフレッシュレートには依存しません。ドリーのシーンはターミナルデモのフレーム単位で定義されているので、ページは 30 fps で時刻をフレーム番号に変換し、

k=⌊τ1000/30⌋,k = \left\lfloor \frac{\tau}{1000/30} \right\rfloor ,

フレーム kk を描画します。60 Hz のディスプレイではドリーの各フレームが 2 回表示され、動きの速さはターミナルと同じです。

構図

キャンバスは 640 × 480(4:3)、センサーはフルサイズ(3:2)です。投影スケール s=Hf/hs = H f / h では、view の設計で導いたとおり、垂直方向の画角がキャンバスの高さを満たし、水平方向の視野はレンズの水平画角より狭くなります。キャンバスのピクセルは正方形なので、ターミナルと違ってアスペクト補正はしません。

設計上の判断

毎フレーム最初から描画する

コールバックのたびにシーン、描画リスト、キャンバス全体を作り直します。何もキャッシュしないので、プログラムはタイムスタンプと選択の純粋関数のままで、640 × 480 では問題になるコストもありません。

Ref による選択

change ハンドラは選ばれたシーンを Ref に書くだけで、アニメーションループが次のフレームでそれを読みます。描画は 1 か所で行われ、シーンの切り替えにループとの調整は要りません。

正しさと不変条件

  • キャンバスのビットマップは 640 × 480 で、投影のビューポートと一致します。
  • 時刻 τ\tau の画は τ\tau と現在の選択だけで決まります。

採用しなかった案

  • シーンごとに別のページにすると、初期化のコードが重複します。
  • タイムスタンプの代わりに固定のフレームカウンタを使うと、速度がリフレッシュレートに依存してしまいます。

境界

demo_canvas は API を公開せず、ブラウザの外では動かず、再生の制御も提供せず(demo_gsap を参照)、ウィンドウのサイズにも合わせません。