Opublikowano: 22 września 2025 r.
Gdy rozpoczynasz przejście widoku, przeglądarka automatycznie robi zrzuty ekranu elementów oznaczonych tagiem view-transition-name przed przejściem i po nim. Te zrzuty 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 nie we wszystkich. 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 grupy przejścia widoku nie mogą się wzajemnie przycinać. opacity,mask-imageifilter: te efekty są zaprojektowane tak, aby działały na w pełni zrasteryzowanym obrazie drzewa, wpływając na elementy podrzędne, a nie na każdy element z osobna.- 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 pokazuje płaskie pseudodrzewo z elementami, które są przycinane przez element nadrzędny w drzewie DOM. Te elementy tracą przycinanie podczas przejścia widoku, co powoduje uszkodzenie efektu wizualnego.
Zagnieżdżone grupy przejść widoku to rozszerzenie przejść widoku, które umożliwia zagnieżdżanie pseudoelementów ::view-transition-group w sobie. Gdy grupy przejść widoku są zagnieżdżone, można przywrócić efekty takie jak przycinanie podczas przejścia.
Browser Support
Od płaskiego pseudodrzewa do zagnieżdżonego pseudodrzewa
W poniższej wersji demonstracyjnej możesz kliknąć awatar osoby, aby wyświetlić więcej informacji na jej temat. Animacje są obsługiwane przez przejście widoku tego samego dokumentu, które przekształca kliknięty przycisk w okno, przesuwa awatar i nazwę po ekranie oraz przesuwa akapity z okna w górę lub w dół.
Prezentacja na żywo
Nagranie demo
Nagranie demonstracyjne (zwolnione)
Jeśli przyjrzysz się uważnie wersji demonstracyjnej, zauważysz, że występuje problem z przejściem: mimo że akapity z opisem są elementami podrzędnymi elementu <dialog> w DOM, tekst nie jest przycinany przez pole <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 do <dialog> też nic nie daje.
Problem polega na tym, jak przejścia widoku tworzą i renderują pseudodrzewo:
- W pseudodrzewie domyślnie wszystkie migawki są elementami równorzędnymi.
- Pseudodrzewo jest renderowane w pseudoelemencie
::view-transition, który jest renderowany na górze całego dokumentu.
W przypadku tej wersji demonstracyjnej 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
└─ …
Pseudoelementy ::view-transition-group(.text) są elementami nadrzędnymi pseudoelementu ::view-transition-group(card), więc są renderowane na karcie.
Aby ::view-transition-group(card) przycinał ::view-transition-group(.text), pseudoelementy ::view-transition-group(.text) powinny być elementami podrzędnymi ::view-transition-group(card). W tym celu użyj view-transition-group, co pozwoli Ci przypisać „grupę nadrzędną” do wygenerowanego pseudoelementu ::view-transition-group().
Grupę nadrzędną możesz zmienić na 2 sposoby:
- W elemencie nadrzędnym ustaw
view-transition-groupnacontain, aby zawierał wszystkie elementy podrzędne zview-transition-name. - We wszystkich elementach podrzędnych ustaw
view-transition-groupnaview-transition-nameelementu nadrzędnego. Możesz też użyćnearest, aby kierować reklamy na najbliższą grupę nadrzędną.
W tym przykładzie, aby użyć zagnieżdżonych grup przejść widoku, kod będzie 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) są teraz zagnieżdżone w pseudoklasie ::view-transition-group(card). Odbywa się to w dodatkowym pseudoelemencie ::view-transition-group-children(…), który utrzymuje wszystkie zagnieżdżone pseudoelementy razem:
html
├─ ::view-transition
│ ├─ ::view-transition-group(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-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)
├─ head
└─ body
└─ …
Na koniec, aby pseudoelement ::view-transition-group(card) przycinał akapity, zastosuj overflow: clip do pseudoelementu ::view-transition-group-children(card):
::view-transition-group-children(card) {
overflow: clip;
}
Wynik jest następujący:
Prezentacja na żywo
Nagranie demo
Nagranie demonstracyjne (zwolnione)
Pseudonim ::view-transition-group-children jest obecny tylko wtedy, gdy używane są grupy zagnieżdżone. Ma on rozmiar pola obramowania oryginalnego elementu i przezroczyste obramowanie o tym samym kształcie i grubości co element, który wygenerował pseudoelement – w poprzednim przykładzie jest to card.
przycinanie i inne funkcje.
Zagnieżdżone grupy przejść widoku są używane w miejscach innych niż efekty przycinania. Innym przykładem są efekty 3D. W poniższej wersji demonstracyjnej dostępna jest opcja obracania karty w 3D podczas przejścia.
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 nazwa nie obracają się wraz z kartą.
Prezentacja na żywo
Nagranie demo
Nagranie demonstracyjne (zwolnione)
Efekt 3D można przywrócić, zagnieżdżając pseudoelementy awatara i nazwy w karcie. To jednak nie wszystko. Oprócz rotacji pseudonimów ::view-transition-old(card) i ::view-transition-new(card) musisz też przeprowadzić rotację pseudonimu ::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 demo
Nagranie demonstracyjne (zwolnione)
Więcej wersji demonstracyjnych
W przykładzie poniżej użyto zagnieżdżonych grup przejść widoku, aby karty były przycinane przez ich nadrzędny element przewijany. Możesz włączyć lub wyłączyć używanie zagnieżdżonych grup przejść widoku za pomocą dołączonych elementów sterujących.
Prezentacja na żywo
Nagranie demo
Ciekawą rzeczą w tym przykładzie jest to, że wszystkie pseudoelementy ::view-transition-group(.card) są zagnieżdżone w pseudoelemencie nadrzędnym ::view-transition-group(cards) i przycinane przez niego. 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 widoków umożliwiają zachowanie części topologii drzewa DOM podczas tworzenia pseudoelementów. Dzięki temu można uzyskać różne efekty, które wcześniej nie były możliwe do osiągnięcia za pomocą przejść widoku. Niektóre z nich opisaliśmy tutaj.
Zagnieżdżanie zmienia model tworzenia przejść widoku i służy do tworzenia zaawansowanych efektów. Jak wspomniano, przejścia widoku ograniczone do 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.