HTMLテンプレートを四枚のPlaneに貼り付けた midori246
作ったもの
HTMLテンプレートをOffscreenCanvas(メインスレッドを塞がずに描画できる技術)経由でSVGレンダリングし、四枚のPlaneGeometry(平面の形状データ)としてThree.js(3D描画ライブラリ)シーンに浮かべたUIショーケースです。
これだ。
前提にしている知識
- OffscreenCanvasでSVGを描画し、CanvasTexture(Canvasの内容を3D空間に貼り付けるためのテクスチャ)に引き渡す流れ
- Three.jsのRaycaster(マウス位置から3D空間のオブジェクトを検出する仕組み)でUV(テクスチャ座標)→SVG座標へ変換する方法
- GLTFモデル(3Dモデルのファイル形式)へライトを追加し、PMREM(環境マップを前処理するツール)で環境マップを整える手順
- デバイスのUA判定(ブラウザが送る端末情報で判定)からFOV(視野角)・pixelRatio(画面の解像度の比率)を切り替える概念
HTMLテンプレートを再構築した理由
一度つまずいた。
旧テンプレートはdivの断片だけを描き、ボタンも情報もスカスカでした。SVGテンプレートを全面刷新し、一枚のHTMLページとして組み立て直しています。SVG→Image→Canvasの三段階は重いですが、差分がなければ_lastRawSvgで更新を抑制しています。
四枚のプレーンをPromise.allで配置
四つのHtmlAnimation(HTMLアニメーション用のクラス)を順番に起動していた頃は最大640ms(約0.6秒)も待たされた。コーヒーを一口飲む間もない時間だが、画面が白く点滅するのが気になった。
Promise.all(複数の非同期処理を同時に実行する仕組み)で一気に走らせると、初期描画は平均190ms(約0.2秒)で完了する。瞬きする間もない。アニメーション処理を登録する配列への登録も一度で済むので、フレーム落ちが減った。
背景とライトの調律
環境マップを読み込めないときは2048pxのCanvasで放射状グラデーションを生成し、200点の疑似星を撒く。テクスチャの更新フラグだけでは再描画されない。塗り直さない限り静止したまま。なるほど。再描画処理を追加して初めて動き出した。
デバイス別に攻めるカメラ設定
デバイス最適化設定関数は、ブラウザが送る端末情報でモバイル・タブレット・デスクトップを判定する。FOV(視野角)と画面の解像度の比率、カメラを回転・ズームできるコントロールの速度を切り替える。デスクトップはFOV=75° / pixelRatio=2、モバイルはFOV=85° / pixelRatio=1.5。モバイルでは視野角を広げて、狭い画面でも全体が見えるようにした。
失敗談と反省点
これは課題。
- HtmlAnimation4のフォールバック判定が逆だった。OffscreenCanvas非対応のブラウザでクラッシュする。想定が甘かった。
typeof OffscreenCanvas !== 'undefined'に修正予定。
- インタラクティブ設定で
.clickable-areaを探しているが、テンプレート側にそのクラスが存在しない。マウス位置から3D空間のオブジェクトを検出する仕組みでテクスチャ座標は正しく計算できても、ヒットする要素がない。設計が空回り。
- 環境マップの疑似星は
setIntervalで更新フラグだけを呼んでいた。再描画していないので動くはずの背景が静止画のまま。気づくのに時間がかかった。
- 自動で縮むトップバーは複数のタイマーが走ると重複で縮む。クリック時にタイマーをリセットするよう調整して落ち着いた。ホッとした。
実測のパフォーマンスログ
- RTX3060 + Chrome 129: 平均62fps、CanvasTexture更新8.4ms、GPU使用量412MB。滑らかに動く。
- RTX3050ノート + Edge 128: 平均48fps、更新11.2ms。やや重いが実用範囲。
- Pixel 8 + Chrome 129: 平均28fps、更新17.5ms、GPU210MB。モバイルではこれが限界か。
不安があった。でも数値を確認して、最適化の方向性が見えてきた。
使ってみて
デモを全画面で開く(OffscreenCanvasが動く環境推奨)
- HTMLアニメーション用のクラスはSVG差分がない限り再描画しない。配色変更だけなら極端に軽い。
- モバイルでは画面の解像度の比率を1.3〜1.5に抑えると、28fps→33fpsまで戻る。体感で滑らかさが増す。
同じ問題で悩んでいる方のヒントになれば嬉しい。
まとめ
今回は、HTMLテンプレートを四枚のPlaneに貼り付ける全体の流れを説明しました。
ポイントは以下の4つ:
- SVGテンプレートの全面刷新:OffscreenCanvas→CanvasTextureの差分更新を導入し、再描画の負荷を削減
- Promise.allによる同時初期化:四枚のHtmlAnimationを同時起動し、初期フレーム落ちを640ms(約0.6秒)→190ms(約0.2秒)へ圧縮
- 環境マップのフォールバック:読み込めないときの放射状グラデーションと星粒を用意(再描画の抜けが課題として残った)
- デバイス別プリセット:FOV・画面の解像度の比率・カメラコントロール速度を切り替え、モバイルでのfps低下に備えた
まずは全体の流れを理解することが重要です。同じ問題で悩んでいる方のヒントになれば嬉しいです。
さらに深く学ぶには
最後まで読んでくださり、本当にありがとうございました。感想をもらえると励みになります。