お疲れ。ビュー遷移API(View Transitions API)の波、ちゃんとキャッチできてるか?
SPA(シングルページアプリケーション)の画面遷移はもちろん、ついにマルチページ(MPA)でもバリバリ動くようになって、俺たちのCSSライフもようやく次のステージに入ったって感じだよな。従来の「ガチャッ」と切り替わるだけのドライな画面遷移から、滑らかで文脈を感じさせるUXへ——。これをCSSだけで手懐けられるんだから、使わない手はない。
さて、今回深掘りするのは、そのView Transitionsの裏側でコッソリ、だけどめちゃくちゃ重要な役割を果たしている`::view-transition-image-pair`擬似要素だ。
「名前は聞いたことあるけど、なんとなくフレームワークのデフォルトに頼ってる」
「スナップショットのサイズが崩れたとき、どこを触ればいいか迷う」
もし君が今、そんなモヤモヤを抱えているなら、この解説で綺麗に晴らしてやろう。ブラウザの内部挙動から現場で使える実践テクニックまで、シニアの俺がみっちり叩き込んでやるから、コーヒーでも飲みながら聞いてくれ。
—
1. `::view-transition-image-pair` とは何か?(ブラウザの裏側で何が起きているか)
まず大前提として、View Transitions APIが発動したとき、ブラウザは裏側で何をやっているか知ってるか?
魔法みたいに見えるかもしれないが、実はブラウザは遷移前のDOMの「見た目(スクリーンショット)」をキャプチャし、遷移後のDOMの「見た目」もキャプチャして、それらを重ね合わせてアニメーションさせている。要は、ただの「画像同士のクロスフェード」に一時的にすり替えているんだ。
そのとき、ブラウザが自動生成するシャドウDOMツリーの構造は、ざっくり言うとこんな感じになっている。
::view-transition
├── ::view-transition-group(root または 任意の名字)
│ ├── ::view-transition-old(root) ← 遷移前の画面(画像)
│ └── ::view-transition-new(root) ← 遷移後の画面(画像)
……おっと、今日の主役を飛ばすところだった。正確には、`group` の直下にワンクッション挟まるんだよ。こうだ。
::view-transition
├── ::view-transition-group(my-element)
│ └── ::view-transition-image-pair(my-element) ← ★ここ!
│ ├── ::view-transition-old(my-element) ← 遷移前のスナップショット
│ └── ::view-transition-new(my-element) ← 遷移後のスナップショット
そう、`::view-transition-image-pair` は、「古い画面(old)」と「新しい画面(new)」のペアをすっぽり包み込む、いわば「コンテナ(箱)」の役割を持っている。
こいつが存在するおかげで、ブラウザは遷移前後の画像を同じ空間に綺麗に並べ、サイズやアスペクト比の計算を安全に行うことができる。いわば、舞台の上の「二人羽織」みたいなもんだ。この箱のサイズや振る舞いをコントロールできるかどうかが、リッチなトランジションを作れるかどうかの分かれ道になる。
—
2. なぜ `::view-transition-image-pair` を触る必要があるのか?
「ブラウザが勝手にやってくれるなら、わざわざ触る必要なくない?」と思ったそこの君。甘いな。
実務でビュー遷移を組んでみると、こんな壁にぶぶつかる。
- 「画面遷移の前後で要素のサイズが変わる時、画像が引き伸ばされてカッコ悪い」
- 「クロスフェードの時に、oldとnewのサイズ違いで変な余白やチラつきが出る」
こういうとき、`::view-transition-old` や `::view-transition-new` だけをいじっても、根本的な解決にならないことが多い。なぜなら、それらの画像が配置される「基準の箱(コンテナ)」の振る舞いがデフォルトのままだからだ。
ここで `::view-transition-image-pair` に対して適切なCSSを当ててやることで、サイズ変動の吸収や、変形(Transform)の基準点をビシッと固定できるようになる。
—
3. 【実践】コピペで使える!リッチなトランジションを創るCSSスニペット
百聞は一見に如かずだ。現場でよくある「カードから詳細画面へ滑らかに拡大する」ようなインタラクションを想定した、実用的なCSSコードを見てくれ。
/ 1. トランジション全体に一意の名前を付与する(HTML側で view-transition-name: product-card; を指定しておくこと) /
.product-card {
view-transition-name: product-card;
}
/ 2. アニメーション全体の時間をカスタムプロパティで定義しておく /
::view-transition-group(product-card) {
animation-duration: 0.4s;
animation-timing-function: cubic-bezier(0.25, 1, 0.5, 1);
}
/ 3. ★ここが本日のハイライト!画像ペアコンテナの制御 /
::view-transition-image-pair(product-card) {
/ デフォルトでは絶対配置などで重なるが、必要に応じてisolationを与えて描画コンテキストを安定させる /
isolation: isolate;
}
/ 4. 遷移前の画像(old)の振る舞い /
::view-transition-old(product-card) {
/ フェードアウトしつつ、元のサイズを維持 /
animation: 0.4s ease-in-out both fade-out;
}
/ 5. 遷移後の画像(new)の振る舞い /
::view-transition-new(product-card) {
/ フェードインしつつ、拡大アニメーションに追従 /
animation: 0.4s ease-in-out both fade-in;
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
チーフアーキテクトからのワンポイント・アドバイス
このコードの肝は、`::view-transition-image-pair` 自体には派手なアニメーションをさせず、その内部の `old` と `new`、そして大元の `group` との役割分担を明確にしている点だ。
特に、要素の大きさがガッツリ変わる遷移(サムネイル ⇒ 拡大画像など)の場合、`::view-transition-image-pair` のデフォルトのレイアウトアルゴリズム(`position: absolute` で親のサイズに追従する挙動)のおかげで、ブラウザが自動的にスケール(拡大・縮小)の補間計算をしてくれる。
もしここで意図しないレイアウト崩れが起きたら、まず疑うべきは `image-pair` ではなくその親の `group` のサイズ指定ミスだが、「oldとnewの位置や重なり順を綺麗にコンテインしたい」というときは、この `::view-transition-image-pair` に対して `mix-blend-mode` や `isolation` を仕込むのがプロの技だ。
—
4. デバッグのコツと現場の落とし穴
最後に、実務でハマりがちなポイントを共有しておく。
1. DevToolsでのセレクタの探し方
ChromeのDevToolsで擬似要素の中身を覗くときは、「Settings(歯車マーク)」から “Show user agent shadow root”(ユーザーエージェントのシャドウDOMを表示) にチェックを入れておく必要がある。これを忘れると、`::view-transition` の中身が一切見えなくて発狂することになるから覚えておけ。
2. アニメーションのパフォーマンス
ビュー遷移中の要素は、実質的にブラウザが生成した「ただの画像(rasterized image)」だ。そのため、重いCSSプロパティ(`width`や`height`など)をアニメーションさせると普通にガタつく。サイズ変更を伴う場合でも、極力ブラウザの自動補間に任せるか、`transform: scale()` 系で処理されるように `group` 側を設計するのが、60fps/120fpsを死守するシニアの流儀だ。
—
まとめ
`::view-transition-image-pair` は、普段のコーディングでは滅多に直接触る必要がない「縁の下の力持ち」的存在かもしれない。しかし、ブラウザが裏側でどうやって画面のトランジションを構築しているかという「解剖学的理解」を持っていなければ、複雑なアニメーションのバグを踏んだときに手も足も出なくなる。
「なぜこの画像ペアは変な伸び方をするのか?」
「どこを起点にクロスフェードしているのか?」
そう迷ったときは、思い出してほしい。その真ん中には、いつも静かに構える `::view-transition-image-pair` がいる。こいつの役割を正しく理解し、君のCSSの引き出しを一つ増やしてやってくれ。
それじゃ、次のレビューでイケてるアニメーションを見せてくれるのを期待してるぜ。

コメント