HTMLプレーンを四層重ねて分裂させる3Dインターフェース実験
HTMLを四枚、同時に3Dへ。並列初期化から断片化アニメまで、一気に通します。進捗式のバグで演出が崩れた経緯と、その直し方も正直に。
HTMLを3D空間へ持ち込む試みの途中版。四枚のプレーンを同時に動かした。失敗も派手だった。焦ったけど対処。
前提で押さえておきたいこと
- Three.js: PlaneGeometry と CatmullRomCurve3 を組み合わせたシーン構築
- OffscreenCanvas: SVGをキャンバスに描き、CanvasTextureへ変換する入口
- Raycaster: UV座標をSVG座標へ変換してボタン位置を復元
体験デモを順番に触れば全体像は掴めるように整えた。
四枚のHTMLプレーンを一斉に起動する
setupInteractiveUI()で4つのモジュールを並列起動している。ギャラリーを担当する Photo_html_Display、動画プレイヤーの mp4_control_Display、静的パネルの static_html_Display と static_html_Display2 の4枚だ。Promise.allで同時に初期化し、3D空間ではそれぞれ scale=2、planeWidth=150 のプレーンに貼っている。
初期配置は次のとおり。
- ギャラリー:
(0, 0, 0)
- 動画プレイヤー:
(50, 350, -100)
- 分裂させる静的パネル:
(0, -150, 30)
- 注意書きパネル:
(300, -100, 50)
Raycasterはどのプレーンにも届くので、ポインタ位置さえ計算できれば一枚ずつ独立したUIになる仕組みだ。
断片化アニメーションで噴き出したバグ
分裂アニメーションは triggerFragmentation() が担う。コメントには「4×4」と書きながら実際は rows=12、cols=12。つまり144断片を生成している。距離は 100 ± 20。回転は各軸ごとに ±2π。
ところが進捗の計算式が異常だった。前半は progress = t / 0.08、後半は progress = (t - 0.5) / 0.01。10秒のはずが0.5秒で6倍外挿し、0.51秒でほぼ初期位置に戻る。これは課題。
もともと静的なパネルを派手に飛ばしたかったのだが、これでは余韻が全く残らない。進捗係数を0.5で割り、戻すときも0.5で割るべきだった。意外でした。
断片は cube をクリックしたときだけ生成される。statichtml2 側の interactiveConfig が空なので、緑のボックスを押しても何も起きない。そこで BoxGeometry を (300, -100, 50) に置き、これをユーザーのトリガーにした。暫定対応だが、クリスタルっぽい立方体を押すと分裂する演出になった。
ギャラリーは300pxに縮小してBASE64化
Photo_html_Display は読込時に画像を convertImageToBase64(url, maxWidth = 300) へ通す。1920×1080の写真でも幅300、高さ約169まで縮小し、そのままBASE64で埋め込む。オフライン表示には便利だが、解像感は大幅に落ちる。懸念があった。
動画プレイヤーも OffscreenCanvas へ描画している。レイアウトでは .video-container の幅を本体の 45% に固定し、実サイズで見ると横 864px、縦 486px。mp4_control_Display は _getVideoContainerBounds() でこの範囲を計測してから drawImage している。
デバイス別チューニングの値
setupDeviceOptimization() ではデバイス判定の後、FOVとカメラ位置を再設定している。デスクトップでは z=50 まで後退、一方モバイルでは z=15 まで近づく。pixelRatio も 1.5〜2.0 の間で切り替えている。ヨシ。
CatmullRomCurve3 で球体を舞わせる
setupColorfulSpheres() は CatmullRomCurve3(true) に50個の球体を載せている。速度は 0.001〜0.003。制御点は3点のベースに乱数で3点追加して閉ループ化。GPU負荷はそこそこあるが、見た目が華やかになる。
120fps設定と描画予算
mp4_control_Display.fps = 120 なので、update() は 8.33ms 以内に OffscreenCanvas の描画と動画の drawImage を終える必要がある。実際はGPUもCPUもそこまで速くない。fpsを下げれば余裕が生まれるが、現状はハードルが高い設定になっている。
HTML→Canvas→3D→爆散の流れ
最後に流れを整理する。SVGテンプレートを描画→CanvasTextureでプレーン化→Raycasterで操作→cubeヒットで断片化。このシーケンス自体はシンプルだが、進捗係数のバグで演出が壊れた。改修前提の検証という位置づけだ。
使ってみて
実際に試してみてください:
デモを起動する(全画面表示)
ポイントは以下の3つ:
- 分裂アニメの進捗係数を見直すと演出が安定(外挿→即戻りを回避)
- 画像は300px BASE64化で帯域優先(解像感とのトレードオフ)
- デバイス別にFOVとpixelRatioを調整してUIを確保
触ってみて
同じように3D空間でHTMLを扱う人のチェックリストになるはずだ。
手応え。
まとめ
- 四枚のHTMLプレーンを
Promise.all で並列初期化し、Raycasterで個別に操作できるようにした。
triggerFragmentation() が 12×12=144 断片を生成するが、進捗計算が 0.08 / 0.01 になっており外挿→即戻りという挙動になった。
- ギャラリー画像は幅300pxまで縮小してBASE64化。高品質な写真でもかなり粗いテクスチャになる。
mp4_control_Display の fps=120 は 8.33ms の描画予算しかなく、動画描画の重さがボトルネックになりやすい。
- デバイス別に FOV と camera.z を変え、モバイルでは z=15 まで寄せてUIを確保している。
さらに深く学ぶには
最後まで読んでくださり、ありがとうございました。