Опубликовано: 22 сентября 2025 г.
При запуске перехода между представлениями браузер автоматически делает снимки элементов, отмеченных тегом view-transition-name
до и после изменения. Эти снимки отображаются в виде дерева псевдоэлементов. По умолчанию сгенерированное дерево является «плоским». Это означает, что исходная иерархия в DOM теряется, и все захваченные группы переходов между представлениями являются одноуровневыми и находятся под одним псевдоэлементом ::view-transition
.
Этот подход с использованием плоского дерева достаточен для многих случаев использования, но есть некоторые случаи стилизации, которые он не может реализовать. Ниже приведены примеры эффектов, которые могут создать неожиданный визуальный эффект в плоском дереве:
- Отсечение (
overflow
,clip-path
,border-radius
): отсечение влияет на дочерние элементы элемента, что означает, что родственные группы перехода вида не могут отсечь друг друга. -
opacity
,mask-image
иfilter
: аналогично, эти эффекты предназначены для работы с полностью растровым изображением дерева, влияя на дочерние элементы, а не на каждый элемент по отдельности. - 3D-преобразования (
transform-style
,transform
,perspective
): для отображения всего спектра анимаций 3D-преобразований необходимо поддерживать определенную иерархию.
В следующем примере показано плоское псевдодерево с элементами, обрезанными предком в DOM-дереве. Эти элементы теряют обрезание при переходе между представлениями, что приводит к искажению визуального эффекта.
Вложенные группы переходов между представлениями — это расширение для переходов между представлениями, позволяющее вкладывать псевдоэлементы ::view-transition-group
друг в друга. Вложенные группы переходов между представлениями позволяют восстанавливать такие эффекты, как обрезка во время перехода.
Browser Support
От плоского псевдодерева к вложенному псевдодереву
В следующем примере вы можете нажать на аватар человека, чтобы увидеть дополнительную информацию о нём. Анимация реализуется с помощью перехода между представлениями в том же документе, который преобразует нажатую кнопку в диалоговое окно, перемещает аватар и имя по экрану, а также сдвигает абзацы диалогового окна вверх или вниз.
Живая демонстрация
Демо-запись
Демо-запись (замедленная)
Если внимательно посмотреть на демо, то можно увидеть, что с переходом есть проблема: даже несмотря на то, что абзацы с описанием являются дочерними элементами элемента <dialog>
в DOM, текст не обрезается блоком <dialog>
во время перехода:
<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>
Применение overflow: clip
к <dialog>
также ничего не дает.
Проблема заключается в том, как переходы между представлениями создают и отображают свое псевдодерево:
- В псевдодереве по умолчанию все снимки являются родственными друг другу.
- Псевдодерево отображается в псевдоэлементе
::view-transition
который отображается поверх всего документа.
Конкретно для этой демонстрации дерево DOM выглядит следующим образом:
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
└─ …
Поскольку псевдоэлементы ::view-transition-group(.text)
являются последующими элементами псевдоэлемента ::view-transition-group(card)
, они рисуются поверх карты.
Чтобы ::view-transition-group(card)
обрезал ::view-transition-group(.text)
, псевдоэлементы ::view-transition-group(.text)
должны быть дочерними по отношению к ::view-transition-group(card)
. Для этого используйте view-transition-group
, который позволяет назначить «родительскую группу» для сгенерированного псевдоэлемента ::view-transition-group()
.
Чтобы изменить родительскую группу, у вас есть два варианта:
- На родительском элементе установите
view-transition-group
значениеcontain
, чтобы он содержал все дочерние элементы сview-transition-name
. - Для всех дочерних элементов установите
view-transition-group
равнымview-transition-name
родительского элемента. Также можно использоватьnearest
для выбора ближайшей родительской группы.
Таким образом, для этой демонстрации, чтобы использовать вложенные группы переходов представлений, код будет следующим:
button.clicked,
dialog {
view-transition-group: contain;
}
Или
button.clicked,
dialog *,
view-transition-group: nearest;
}
Благодаря этому коду псевдоэлементы ::view-transition-group(.text)
теперь вложены в псевдоэлемент ::view-transition-group(card)
. Это реализовано в дополнительном псевдоэлементе ::view-transition-group-children(…)
, который объединяет все вложенные псевдоэлементы:
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
└─ …
Наконец, чтобы псевдоэлемент ::view-transition-group(card)
обрезал абзацы, примените overflow: clip
к псевдоэлементу ::view-transition-group-children(card)
:
::view-transition-group-children(card) {
overflow: clip;
}
Результат следующий:
Живая демонстрация
Демо-запись
Демо-запись (замедленная)
Псевдоэлемент ::view-transition-group-children
присутствует только при использовании вложенных групп. Он имеет размер, соответствующий границе исходного элемента, и прозрачную рамку той же формы и толщины, что и элемент, сгенерировавший этот псевдоэлемент — card
в предыдущем примере.
Вырезание и многое другое
Вложенные группы переходов видов используются не только в эффектах обрезки. Другой пример — 3D-эффекты. В следующей демонстрации показана возможность вращать карту в 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;
}
}
Без вложенных групп перехода видов аватар и имя не будут вращаться вместе с карточкой.
Живая демонстрация
Демо-запись
Демо-запись (замедленная)
Вложив псевдоаватар и имя в карточку, можно восстановить 3D-эффект. Но это не единственное, что нужно сделать. Помимо поворота псевдоэлементов ::view-transition-old(card)
и ::view-transition-new(card)
, необходимо также повернуть псевдоэлемент ::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;
}
}
Живая демонстрация
Демо-запись
Демо-запись (замедленная)
Больше демо
В следующем примере вложенные группы переходов видов используются для обеспечения обрезки карточек родительским скроллером. Вы можете включать и отключать использование вложенных групп переходов видов с помощью встроенных элементов управления.
Живая демонстрация
Демо-запись
Интересно в этой демонстрации то, что все псевдоэлементы ::view-transition-group(.card)
вложены в предковый псевдоэлемент ::view-transition-group(cards)
и обрезаются им. #targeted-card
исключен, поскольку его анимация входа/выхода не должна обрезаться псевдоэлементом ::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;
}
Резюме
Вложенные переходы между представлениями позволяют сохранить часть топологии DOM-дерева при построении псевдоэлементов. Это открывает доступ к множеству эффектов, ранее недоступных при переходах между представлениями, некоторые из которых мы описали здесь.
Вложенность изменяет модель построения переходов между представлениями и предназначена для создания сложных эффектов. Как уже отмечалось, переходы между представлениями на уровне элементов также позволяют реализовать часть эффектов, используя более простую модель. Мы рекомендуем вам попробовать обе функции, чтобы решить, какая из них лучше всего подходит вам.