CSS と疑似要素
View Transition のカスタマイズはほぼ全部 CSS 側で行う。
名前を付けて、疑似要素を @keyframes でターゲットする、これさえ覚えれば応用が利く。
疑似要素ツリーの全体像
transition が始まると、ブラウザは以下のツリーを ::view-transition として
ドキュメントの最前面(::backdrop の上)に組み立てる。
::view-transition ← 全体のルート(fixed, inset:0)
└─ ::view-transition-group(root) ← グループ。位置/サイズを補間
└─ ::view-transition-image-pair(root) ← old/new を重ねる箱(mix-blend)
├─ ::view-transition-old(root) ← 古いスナップショット画像(fade-out)
└─ ::view-transition-new(root) ← 新しいスナップショット画像(fade-in)
# view-transition-name を付けた要素ごとに同じ階層が増える
└─ ::view-transition-group(card-3)
└─ ::view-transition-image-pair(card-3)
├─ ::view-transition-old(card-3)
└─ ::view-transition-new(card-3)
各階層の役割
| 疑似要素 | 担当 |
|---|---|
::view-transition | 全体のオーバーレイ。普段はいじらない。 |
::view-transition-group(name) | 位置・サイズの補間を担当。width/height/transform がここに animation として乗る。 |
::view-transition-image-pair(name) | old と new を重ねる箱。基本は isolation: isolate で blend を分離する用途。 |
::view-transition-old(name) | 古いスナップショット画像。デフォルトで fade-out。 |
::view-transition-new(name) | 新しいスナップショット画像。デフォルトで fade-in。 |
view-transition-name
ある要素を独立した snapshot ペアとして扱うためのフラグ。
値は文字列の識別子(または none / auto)。
遷移前後で同じ名前の要素はペアにされ、位置とサイズが補間される。
.card-hero {
view-transition-name: card-hero;
}
- 名前は同一フレーム内で一意。複数要素に同じ名前が当たると transition 自体がスキップされる。
noneを指定すると、デフォルトで継承されてしまうケースを抑制できる。auto(対応ブラウザのみ)を使うと、要素ごとに自動でユニークな名前を割り当てる。SPA でリスト要素ごとに動的な名前が欲しい時に便利。
リストに対して .card { view-transition-name: card; } のように静的に同じ名前を当てると、
要素が複数になった瞬間に名前衝突で transition 全体がスキップされる。
リストには auto か、style="view-transition-name: card-3" のようにidベースで動的に付ける。
デフォルトのアニメーション
ユーザー側で何も書かなくても、ブラウザは以下を勝手にやる:
::view-transition-old(root)に fade-out(opacity 1 → 0)::view-transition-new(root)に fade-in(opacity 0 → 1)view-transition-name付き要素では、加えてgroup(name)に 位置・サイズの補間(top/left/width/height を transform 等で)- 長さは 250ms、イージングは ease 系
カスタマイズ — 期間・カーブだけ変える
::view-transition-group(*) {
animation-duration: 400ms;
animation-timing-function: cubic-bezier(.2, .8, .2, 1);
}
(*) はワイルドカードで、すべての group をターゲットにする。
個別に対象を絞る時は ::view-transition-group(card-hero) のように名前を渡す。
カスタマイズ — アニメーション自体を差し替える
::view-transition-old/new は普通の要素として扱える。
@keyframes を書いて animation を上書きすればいい。
@keyframes slide-out-left {
to { transform: translateX(-30%); opacity: 0; }
}
@keyframes slide-in-right {
from { transform: translateX(30%); opacity: 0; }
}
::view-transition-old(root) {
animation: slide-out-left 300ms ease both;
}
::view-transition-new(root) {
animation: slide-in-right 300ms ease both;
}
old を後ろに残し、new を上から重ねる、というような演出は両方に animation を当てて
同じ duration を使うのが基本。
片方を「先に消えて」「後から出てくる」順序にしたい時は animation-delay で時間差をつける。
カスタマイズ — モーフ(位置・サイズ補間)
view-transition-name を付けた要素は、デフォルトで group(name) に
位置・サイズの補間アニメが乗る。クロスフェード前提のままだと違和感がある場合は、
image-pair 配下の old/new の不透明度を弄って繋ぎ方を作り直すのが定石。
::view-transition-old(card-hero) {
/* 古い画像は描かない(位置補間は group が引き続きやる) */
animation: none;
opacity: 0;
}
::view-transition-new(card-hero) {
animation: none;
opacity: 1;
}
::view-transition-group() を直接動かす
位置補間そのものを書き直すこともできる。ただし width/height/transform を全部自分で制御するのは大変なので、 実用は「色だけ動かす」「最後に少しオーバーシュートする」程度の補正に留めるのが楽。
::view-transition-group(card-hero) {
animation-duration: 500ms;
animation-timing-function: cubic-bezier(.16, 1.25, .3, 1); /* overshoot */
}
view-transition-class(Level 2)
Level 2 で導入された、疑似要素をクラス的に一括ターゲットするためのプロパティ。
対応ブラウザでは複数要素に同じ class を付けて、CSS 側で ::view-transition-group(.card-class) のように指定できる。
.card { view-transition-class: card; }
::view-transition-group(.card) {
animation-duration: 350ms;
}
対応していないブラウザでは個別に view-transition-name を当てる必要があるので、
注意点・ブラウザ対応 を見てから使う。
:active-view-transition / :active-view-transition-type()(Level 2)
進行中の transition の types を見て CSS を分岐できる擬似クラス。
「進む」「戻る」で別アニメにしたいときに使う。
document.startViewTransition({
update: () => navigate(),
types: ["forward"],
})
html:active-view-transition-type(forward) {
&::view-transition-old(root) { animation: slide-out-left 300ms; }
&::view-transition-new(root) { animation: slide-in-right 300ms; }
}
html:active-view-transition-type(back) {
&::view-transition-old(root) { animation: slide-out-right 300ms; }
&::view-transition-new(root) { animation: slide-in-left 300ms; }
}
prefers-reduced-motion の取り扱い
デフォルトのフェード自体は控えめだが、独自に派手な演出を当てている場合は必ず減衰版を用意する。
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
完全に止めると遷移瞬間で「カチッ」と切り替わる。
必要なら短いフェードだけは残すのもあり。