View Transitions のパフォーマンス最適化

ジャンクを回避し、スナップショットメモリを削減し、Web Animations API と組み合わせて、中位機種のモバイルでも 60fps の細かく制御されたトランジションを実現。

Integration

詳細な説明

コストモデル

各 View Transition のコスト:

  1. 2枚のラスタースナップショット — 影響を受けるビューポートに比例(ビューポートサイズと device-pixel-ratio)。
  2. named 要素ごとに1つのコンポジターレイヤー — named 要素 10個 = 追加レイヤー 10個。
  3. アニメーション補間 — 通常 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ドル端末)をターゲットにする本番サイトでミリ秒単位の差が重要な場面。複数のトランジションが連続発火する画像エディタやゲームロビーなどトランジションが多用されるアプリでも有用。

試してみるView Transitions API ジェネレーター

フルツールを開く