GENEVISのトップページとCOMPANYページには、Three.jsによる背景演出があります。停止した姿勢から再開すること、演出が使えないときも本文を読めることを実装しました。対象は2026年9月10日時点のコードです。スマートフォン実機の性能や電池消費は未検証です。
再生してよい条件と、描画の処理を分ける
両ページとも、src/scripts/hero-motion.jsで画面内か、タブが表示中か、利用者が停止しているかを判定します。同じディレクトリ内で、シーンの生成、描画、資源の解放はhero-three.js、時間と補間の計算はhero-animation.jsへ分けています。COMPANY用の文字サイズ・粒子数・描画品質などはhero-scene-config.jsで切り替えます。
再生できるのは、次の条件がそろったときです。
- ページ離脱による破棄や、ページ退避による一時停止の状態ではない。
- 停止操作や、動きを減らす設定で再生を抑止されていない。
- ヒーローが画面内にあり、タブも表示されている。
通信節約や低速回線の情報が取得できた場合は、初期状態を停止にします。この判定は取得できるブラウザーの情報に依存するため、すべての端末で同じ回線情報を得られる前提にはしていません。
再生条件がそろってから演出のモジュールを読み込み、完了直後にも条件を確認します。その時点で停止中や画面外ならシーン生成を見送ります。シーン生成内のフォント待機中に停止した場合は、生成完了後に停止を反映します。
自サイト配信へ移しても、描画部分の取得は遅らせる
9月10日に、Three.jsを外部CDNから取得する構成から、npmで管理してサイトと一緒に配信する構成へ変更しました。バージョンは移行前と同じ0.185.1に固定し、package-lock.jsonで管理しています。
演出用の5ファイルをpublic/scripts/からsrc/scripts/へ移し、両ページで共用するHeroMotionScript.astroの<script>から読み込み制御を取り込みます。Astroが処理するスクリプトでは、npmパッケージを含む依存関係をバンドルできます。Astro公式のスクリプト処理の説明
描画側のhero-three.jsは静的に取り込まず、再生条件がそろった後にimport('./hero-three.js')で取得します。生成結果でも読み込み制御と描画部分は別ファイルになり、TOPとCOMPANYが同じ配信用ファイルを参照します。従来のimport mapは不要になりました。
配信場所を変えても、回転の慣性や光の演出、停止・再開の計算は変えていません。外部CDNへの接続をなくすことと、GPUでの描画負荷を減らすことは別の変更です。
停止中の時間を、アニメーションへ足さない
描画の予約を止めるだけでは、再開時に時刻の差をまとめて取り込み、回転や浮遊が先へ飛ぶことがあります。そこで、再生している間だけ増える時間を持たせました。
時計は停止時に累積時間を残し、前フレームの時刻だけを解除します。再開後の最初のフレームは差分を0にして、次から時間を加えます。実装の要点を抜き出すと次の形です。
const delta = previous === null
? 0
: Math.min(100, Math.max(0, now - previous));
previous = now;
time += delta;
この例ではnowとtimeの単位はミリ秒です。長いフレーム停止でも一度に進む量を100ミリ秒に制限しています。実時間へ厳密に追いつくことより、背景の動きが急に飛ばないことを優先した、この演出固有の判断です。
停止中に画面サイズが変わる場合は、描画サイズとカメラを更新して、保持した姿勢を描き直します。再描画のためだけに時計を進める必要はありません。
表示前と障害時の状態を、黒背景として決める
初期状態は黒背景で、本文やナビゲーションは演出と独立したHTMLです。描画が成功してからキャンバスへ表示用のクラスを付けます。
モジュールの取得失敗や描画中の例外では、表示を解除し、シーンの資源を解放します。今回のサイトでは、代わりの動画や静止画像は追加していません。演出が使えなくても、会社情報と相談方法へ進めることを優先しています。
WebGL contextの喪失は、描画エラーによる破棄と分けました。喪失中は停止してキャンバスを隠し、復旧イベントでサイズを再設定します。そのうえで再生要求が残っていれば再開します。動きを減らす設定による非表示も、シーン側で判定を維持します。
一時停止と、資源の解放を区別する
画面外へスクロールしただけで毎回シーンを作り直すと、読み込みや初期化を繰り返すことになります。通常の停止ではシーンを保持し、ページを破棄するときや回復不能なエラーで解放する構成です。
pagehideの際、戻る・進む操作のためにページが保存される場合は一時停止にし、保存されない離脱では破棄します。復帰時は再生条件をもう一度確認します。
Three.jsのgeometry、material、textureなどのGPU資源は、不要になった時点で明示的な解放が必要です。Three.js公式の資源解放に関する説明
この実装では、シーンが管理する資源を集合へ登録し、破棄時にdispose()を呼びます。イベントと監視処理の解除もまとめ、破棄後の二重処理はフラグで防ぎます。requestAnimationFrameの予約取消しも、同じ後片付けに含めています。
確認できたことと、残っている測定
2026年9月10日の配信切り替え後に既存の10テストを再実行し、停止時間の除外、重複開始、30・60・120Hzでの補間、COMPANY用の描画上限と画面内への収まりを確認しました。
生成したサイトをPCのEdgeで開き、TOP・COMPANYの320・390・1280px幅で描画と画面外停止・復帰を確認しました。両ページとも外部への通信はなく、同じ描画ファイルを1回取得しました。停止・再開はPC幅でクリックとキーボードを確認しています。760px以下は既存のデザインで停止ボタンが非表示のため、スマートフォン幅では操作処理を直接呼び出す検証にとどまります。非表示タブとページ退避・復帰は、状態とイベントを模擬して確認しました。
動きを減らす設定・JavaScript無効・通信節約時は描画ファイルを初期取得せず、取得失敗・WebGL無効時は黒背景と本文を維持することも確認しました。通信節約中の再生操作、ダウンロード中に停止してからの再開、WebGL contextの喪失・復旧も、今回の配信構成で検証しています。
過去のブラウザー検証では、トップの停止姿勢・画面外停止・停止中のリサイズ、WebGL contextの喪失と復旧、模擬描画エラー後の表示解除を確認しています。COMPANYでも停止・再開や、JavaScript・WebGLが使えない場合、配信元を遮断した場合の黒背景を確認した記録があります。
PC上でスマートフォン幅を使った確認は、実機の性能測定ではありません。解像度に上限を設けたことだけで、実機の発熱や電池消費が改善したとも言えません。実機での評価は別の作業として残しています。
この背景で再利用したいのは、特定の光や回転の表現より、動ける条件と動けないときの状態を先に決める考え方です。表示に成功した場面だけでなく、停止、復帰、失敗、破棄までを一つの実装として扱います。
失敗時の状態から次の動作を決める別の実装例として、問い合わせフォームの再送と処理状態の管理も記録しています。