Opublikowano: 22 września 2025 r.
Gdy rozpoczniesz przejście widoku, przeglądarka automatycznie zrobi zrzuty elementów oznaczonych atrybutem view-transition-name
przed i po zmianie. Te migawki są renderowane w drzewie pseudoelementów. Domyślnie wygenerowane drzewo jest „płaskie”. Oznacza to, że pierwotna hierarchia w DOM zostaje utracona, a wszystkie przechwycone grupy przejść widoku są elementami równorzędnymi w ramach jednego pseudoelementu ::view-transition
.
Takie podejście sprawdza się w wielu przypadkach, ale w niektórych sytuacjach związanych ze stylem nie można go zastosować. Oto przykłady efektów, które mogą mieć nieoczekiwany efekt wizualny w płaskim drzewie:
- Przycinanie (
overflow
,clip-path
,border-radius
): przycinanie wpływa na elementy podrzędne, co oznacza, że elementy równorzędne w grupie przejścia widoku nie mogą się wzajemnie przycinać. opacity
,mask-image
ifilter
: te efekty są zaprojektowane tak, aby działać na w pełni zrasteryzowanym obrazie drzewa, wpływając na elementy podrzędne, a nie na poszczególne elementy.- Przekształcenia 3D (
transform-style
,transform
,perspective
): aby wyświetlić pełny zakres animacji przekształceń 3D, należy zachować pewną hierarchię.
Poniższy przykład przedstawia płaskie pseudodrzewo z elementami, które są przycinane przez element nadrzędny w drzewie DOM. Podczas przejścia widoku te elementy tracą przycinanie, co powoduje nieprawidłowy efekt wizualny.
Zagnieżdżone grupy przejść widoku to rozszerzenie przejść widoku, które umożliwia zagnieżdżanie ::view-transition-group
pseudo-elementów w sobie. Gdy grupy przejścia widoku są zagnieżdżone, podczas przejścia można przywrócić efekty, takie jak przycinanie.
Browser Support
Od płaskiego pseudodrzewa do zagnieżdżonego pseudodrzewa
W poniższym demo możesz kliknąć awatar osoby, aby wyświetlić więcej informacji na jej temat. Animacje są obsługiwane przez przejście widoku w tym samym dokumencie, które przekształca kliknięty przycisk w okno, przesuwa awatar i imię po ekranie oraz przesuwa akapity z okna w górę lub w dół.
Prezentacja na żywo
Nagranie demonstracyjne
Nagranie demonstracyjne (zwolnione)
Jeśli przyjrzysz się uważnie wersji demonstracyjnej, zauważysz problem z przejściem: mimo że akapity z opisem są elementami podrzędnymi elementu <dialog>
w DOM, tekst nie jest przycinany przez pole elementu <dialog>
podczas przejścia:
<dialog id="info_bramus" closedby="any">
<h2><img alt="…" class="avatar" height="96" width="96" src="avatar_bramus.jpg"> <span>Bramus</span></h2>
<p>Bramus is …</p>
<p>…</p>
</dialog>
Zastosowanie overflow: clip
na <dialog>
również nic nie daje.
Problem polega na tym, jak przejścia widoku tworzą i renderują swoje pseudodrzewo:
- W pseudo-drzewie wszystkie migawki są domyślnie rodzeństwem.
- Pseudo-drzewo jest renderowane w
::view-transition
pseudo-elemencie, który jest renderowany na wierzchu całego dokumentu.
W tym przypadku drzewo DOM wygląda tak:
html
├─ ::view-transition
│ ├─ ::view-transition-group(card)
│ │ └─ ::view-transition-image-pair(card)
│ │ ├─ ::view-transition-old(card)
│ │ └─ ::view-transition-new(card)
│ ├─ ::view-transition-group(name)
│ │ └─ ::view-transition-image-pair(name)
│ │ ├─ ::view-transition-old(name)
│ │ └─ ::view-transition-new(name)
│ ├─ ::view-transition-group(avatar)
│ │ └─ ::view-transition-image-pair(avatar)
│ │ ├─ ::view-transition-old(avatar)
│ │ └─ ::view-transition-new(avatar)
│ ├─ ::view-transition-group(paragraph1.text)
│ │ └─ ::view-transition-image-pair(paragraph1.text)
│ │ └─ ::view-transition-new(paragraph1.text)
│ └─ ::view-transition-group(paragraph2.text)
│ └─ ::view-transition-image-pair(paragraph2.text)
│ └─ ::view-transition-new(paragraph2.text)
├─ head
└─ body
└─ …
Ponieważ pseudoklasy ::view-transition-group(.text)
są kolejnymi elementami po pseudoklasie ::view-transition-group(card)
, są rysowane na wierzchu karty.
Aby klip ::view-transition-group(card)
miał ::view-transition-group(.text)
, pseudoselektory ::view-transition-group(.text)
muszą być elementami podrzędnymi elementu ::view-transition-group(card)
. W tym celu użyj view-transition-group
, który umożliwia przypisanie „grupy nadrzędnej” do wygenerowanego pseudoelementu ::view-transition-group()
.
Aby zmienić grupę nadrzędną, masz 2 możliwości:
- W elemencie nadrzędnym ustaw atrybut
view-transition-group
nacontain
, aby zawierał wszystkie elementy podrzędne z atrybutemview-transition-name
. - We wszystkich elementach podrzędnych ustaw
view-transition-group
naview-transition-name
elementu nadrzędnego. Możesz też użyć znakunearest
, aby kierować reklamy na najbliższą grupę nadrzędną.
W tym przykładzie, aby użyć zagnieżdżonych grup przejść widoku, kod wygląda tak:
button.clicked,
dialog {
view-transition-group: contain;
}
Lub
button.clicked,
dialog *,
view-transition-group: nearest;
}
Po dodaniu tego kodu pseudoklasy ::view-transition-group(.text)
zostaną zagnieżdżone w pseudoklasie ::view-transition-group(card)
. Odbywa się to w dodatkowym pseudoselektorze ::view-transition-group-children(…)
, który utrzymuje wszystkie zagnieżdżone pseudoselektory razem:
html
├─ ::view-transition
│ ├─ ::view-transition-group(card)
│ │ ├─ ::view-transition-image-pair(card)
│ │ │ ├─ ::view-transition-old(card)
│ │ │ └─ ::view-transition-new(card)
│ │ └─::view-transition-group-children(card)
│ │ ├─ ::view-transition-group(paragraph1.text)
│ │ │ └─ ::view-transition-image-pair(paragraph1.text)
│ │ │ └─ ::view-transition-new(paragraph1.text)
│ │ └─ ::view-transition-group(paragraph2.text)
│ │ └─ ::view-transition-image-pair(paragraph2.text)
│ │ └─ ::view-transition-new(paragraph2.text)
│ ├─ ::view-transition-group(name)
│ │ └─ ::view-transition-image-pair(name)
│ │ ├─ ::view-transition-old(name)
│ │ └─ ::view-transition-new(name)
│ └─ ::view-transition-group(avatar)
│ └─ ::view-transition-image-pair(avatar)
│ ├─ ::view-transition-old(avatar)
│ └─ ::view-transition-new(avatar)
├─ head
└─ body
└─ …
Aby element ::view-transition-group(card)
przycinał akapity, zastosuj overflow: clip
do elementu ::view-transition-group-children(card)
:
::view-transition-group-children(card) {
overflow: clip;
}
Wynik jest następujący:
Prezentacja na żywo
Nagranie demonstracyjne
Nagranie demonstracyjne (zwolnione)
Pseudoklasa ::view-transition-group-children
występuje tylko wtedy, gdy używane są grupy zagnieżdżone. Ma rozmiar border-box oryginalnego elementu i przezroczyste obramowanie o takim samym kształcie i grubości jak element, który wygenerował pseudoelement – card
w poprzednim przykładzie.
Wycinanie i inne funkcje
Zagnieżdżone grupy przejść widoku są używane w innych miejscach niż efekty przycinania. Innym przykładem są efekty 3D. W poniższym filmie demonstracyjnym podczas przejścia można obracać kartę w 3D.
html:active-view-transition-type(open) {
&::view-transition-old(card) {
animation-name: rotate-out;
}
&::view-transition-new(card) {
animation-name: rotate-in;
}
}
html:active-view-transition-type(close) {
&::view-transition-old(card) {
animation-name: rotate-in;
}
&::view-transition-new(card) {
animation-name: rotate-out;
}
}
Bez zagnieżdżonych grup przejść widoku awatar i imię nie obracają się wraz z kartą.
Prezentacja na żywo
Nagranie demonstracyjne
Nagranie demonstracyjne (zwolnione)
Umieszczenie pseudoklas awatara i nazwy wewnątrz karty przywraca efekt 3D. To jednak nie wszystko. Oprócz obracania pseudoznaków ::view-transition-old(card)
i ::view-transition-new(card)
musisz też obracać pseudoznak ::view-transition-group-children(card)
.
html:active-view-transition-type(open) {
&::view-transition-group-children(card) {
animation: rotate-in var(--duration) ease;
backface-visibility: hidden;
}
}
html:active-view-transition-type(close) {
&::view-transition-group-children(card) {
animation: rotate-out var(--duration) ease;
backface-visibility: hidden;
}
}
Prezentacja na żywo
Nagranie demonstracyjne
Nagranie demonstracyjne (zwolnione)
Więcej wersji demonstracyjnych
W poniższym przykładzie zagnieżdżone grupy przejść widoku są używane, aby karty były przycinane przez element przewijany nadrzędny. Za pomocą dostępnych elementów sterujących możesz włączać i wyłączać korzystanie z zagnieżdżonych grup przejść widoku.
Prezentacja na żywo
Nagranie demonstracyjne
Ciekawostką w tym przykładzie jest to, że wszystkie pseudoelementy ::view-transition-group(.card)
są zagnieżdżone w pseudoelemencie ::view-transition-group(cards)
elementu nadrzędnego i przez niego przycinane. Element #targeted-card
jest wykluczony, ponieważ jego animacja wejścia/wyjścia nie powinna być przycinana przez element ::view-transition-group(cards)
.
/* The .cards wrapper contains all children */
.cards {
view-transition-name: cards;
view-transition-group: contain;
}
/* Contents that bleed out get clipped */
&::view-transition-group-children(cards) {
overflow: clip;
}
/* Each card is given a v-t-name and v-t-class */
.card {
view-transition-name: match-element;
view-transition-class: card;
}
/* The targeted card is given a unique name (to style the pseudo differently)
and shouldn't be contained by the ::view-transition-group-children(cards) pseudo */
#targeted-card {
view-transition-name: targeted-card;
view-transition-group: none;
}
Podsumowanie
Zagnieżdżone przejścia widoku umożliwiają zachowanie części topologii drzewa DOM podczas tworzenia pseudoelementów. Umożliwia to uzyskanie różnych efektów, które wcześniej nie były możliwe w przypadku przejść widoku. Niektóre z nich opisaliśmy tutaj.
Zagnieżdżanie zmienia model tworzenia przejść widoku i jest przeznaczone do tworzenia zaawansowanych efektów. Jak wspomnieliśmy, przejścia widoku w zakresie elementu mogą też osiągnąć podzbiór efektów za pomocą prostszego modelu. Zachęcamy do wypróbowania obu funkcji, aby zdecydować, która z nich najlepiej odpowiada Twoim potrzebom.