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-imageifilter: 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-transitionpseudo-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-groupnacontain, aby zawierał wszystkie elementy podrzędne z atrybutemview-transition-name. - We wszystkich elementach podrzędnych ustaw
view-transition-groupnaview-transition-nameelementu 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.