View Transitions のパフォーマンス最適化
ジャンクを回避し、スナップショットメモリを削減し、Web Animations API と組み合わせて、中位機種のモバイルでも 60fps の細かく制御されたトランジションを実現。
詳細な説明
コストモデル
各 View Transition のコスト:
- 2枚のラスタースナップショット — 影響を受けるビューポートに比例(ビューポートサイズと device-pixel-ratio)。
- named 要素ごとに1つのコンポジターレイヤー — named 要素 10個 = 追加レイヤー 10個。
- アニメーション補間 — 通常 GPU アクセラレーション、レイヤーが存在すれば安価。
スナップショットキャプチャが支配的なコストです。50個の named 要素を持つ 4K デバイスでは、キャプチャフェーズが 50〜80ms かかることがあり、アニメーション開始前の体感遅延として現れます。
named 要素を減らす
共通要素モーフが必要な要素だけに名前を付けてください。リストアイテムがページの残りとクロスフェードするだけなら、名前なしのままルートグループに入れて無料で済ませられます。
will-change を戦略的に使う
.expensive-card {
view-transition-name: card;
will-change: transform, opacity;
}
will-change はトランジション開始 前に 要素を独自のコンポジターレイヤーに昇格させ、スナップショット中にレイヤー割り当てが必要にならないようにします。トランジション完了後は外します:
const t = document.startViewTransition(update);
await t.finished;
document.querySelectorAll('.expensive-card').forEach(el => el.style.willChange = 'auto');
トランジション中はアニメーションを一時停止
ページに動作中の CSS アニメーション(回転ローダー、マーキー)があれば、トランジション中は paint サイクルを解放するため一時停止します:
:root:has(::view-transition) * {
animation-play-state: paused !important;
}
細かい制御には Web Animations API
擬似要素は WAAPI アニメーションを受け付けます:
const t = document.startViewTransition(update);
await t.ready;
document.documentElement.animate(
{ opacity: [0, 1] },
{
duration: 300,
easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
pseudoElement: '::view-transition-new(root)',
},
);
WAAPI アニメーションは同じ擬似要素の CSS @keyframes を上書きするので、動的な duration やコールバックが必要なときは JS から権威的に制御できます。
プロファイリング
Chrome DevTools → Performance → トランジションを記録。タイムラインの「ViewTransition」イベントがスナップショットキャプチャ時間・レイヤー数・アニメーション duration を表示します。スナップショットキャプチャが 16ms を超えるなら、ビューポートサイズや named 要素数を減らします。
ユースケース
中位機種の Android(例: 4GB RAM の200ドル端末)をターゲットにする本番サイトでミリ秒単位の差が重要な場面。複数のトランジションが連続発火する画像エディタやゲームロビーなどトランジションが多用されるアプリでも有用。