发布时间:2026 年 3 月 27 日
元素范围的视图转换可让多个视图转换同时运行,允许将正在进行的视图转换嵌套在另一个视图转换中,并解决您可能遇到的与文档范围的视图转换相关的 z-index 问题,同时保持页面的其余部分可互动。请参阅本指南,了解如何使用这些功能。
需要范围更窄的视图过渡
当您使用 document.startViewTransition()(或通过其跨文档对应项)启动同文档视图过渡时,浏览器会将生成的视图过渡限定在文档范围内。
在执行更新回调且浏览器对所有必要元素进行快照后,生成的 ::view-transition 叠加层及其伪元素树会附加到 :root 元素(在以下示例中为 html)。
html
├─ ::view-transition
│ └─ ::view-transition-group(root)
│ └─ ::view-transition-image-pair(root)
│ ├─ ::view-transition-old(root)
│ └─ ::view-transition-new(root)
├─ head
└─ body
└─ …
由于 ::view-transition 层在过渡根之上进行渲染,这可能会导致意外情况。例如,参与视图转换的元素可能会突然与其他非参与元素重叠,或者元素在视图转换期间可能不再被其祖先封装容器剪裁。
实时演示
演示录制
在 ::view-transition 上重新启用 pointer-events 或使用嵌套的视图过渡组可以解决文档范围的视图过渡引入的一些副作用。不过,这些方法无法解决所有问题。
例如,在过渡处于活动状态时,具有 position: fixed 或弹出式窗口的元素仍会被文档范围的视图过渡遮挡,这也称为 z-index 问题。
在以下演示中切换弹出式窗口,然后选择随机播放按钮以开始文档范围的视图过渡。嵌套的视图过渡组可解决裁剪问题,但分层问题仍然存在。
实时演示
演示录制
一种解决方法是为 popover 提供 view-transition-name,以在视图转换过程中捕获该 popover。虽然这可能适用于单个实例,但维护起来很麻烦,并且会给创建快照流程带来不必要的压力。
元素范围内的视图过渡
元素范围的视图转换可让您在 DOM 的子树上启动视图转换。您无需调用 document.startViewTransition(),而是对任意元素调用 element.startViewTransition(),这会将视图转换限定为该元素。
在以下代码段中,浏览器在 <ul> 元素上启动了元素范围的视图转换。
document.querySelector('ul').startViewTransition({
callback: () => {
// … code that manipulates the contents of <ul>
},
})
调用 element.startViewTransition() 的元素(例如 <ul>)称为过渡根或范围。
当浏览器将视图过渡限定到某个元素时,该元素会与 DOM 的其余部分隔离开来:
- 浏览器仅在范围的子树中查找要拍摄快照的元素。
- 在拍摄快照的过程中(即
update回调执行期间),只有范围的渲染会停止。 - 生成的
::view-transition伪树会注入到过渡根中。
例如,使用 <ul> 时,在视图过渡处于活动状态期间,DOM 树如下所示:
html
├─ head
└─ body
├─ ul
│ ├─ ::view-transition
│ │ └─ ::view-transition-group(root)
│ │ ├─ ::view-transition-group-children(root)
│ │ │ └─ …
│ │ └─ ::view-transition-image-pair(root)
│ │ ├─ ::view-transition-old(root)
│ │ └─ ::view-transition-new(root)
│ ├─ li
│ ├─ li
│ └─ li
├─ button#showpopover
├─ button#reorder
└─ div#popover
└─ p
::view-transition 伪对象的大小和形状与过渡根相同,并且仅在过渡根之上渲染。因此,系统会遵循过渡根之外元素的层叠顺序。
例如,如果您有一个显示在 <ul> 元素上方的弹出式窗口,然后对 <ul> 元素启动元素范围的视图过渡,则弹出式窗口不会被视图过渡的伪树遮挡。
在以下演示中,您可以试用一下。它有两个按钮。第一个按钮用于切换弹出式窗口,第二个按钮使用元素范围的视图转换重新排序列表项。
实时演示
演示录制
由于使用了元素范围的视图转换,因此在转换处于活动状态时,弹出式窗口会保持在 <ul> 元素上方可见。
此外,<ul> 元素之外的元素(例如按钮)仍保持互动性,因为这些元素不属于范围的一部分。
自参与范围和嵌套视图转换组
当您对剪裁溢出内容的元素(即 overflow 设置为 hidden、scroll 或 clip)启动元素范围的视图转换时,您会发现视图转换的内容在视觉上仍处于剪裁状态。
这是因为元素范围的视图过渡会自动处理以下事项:
- 范围会自动应用
view-transition-name: root,使其成为自参与范围。 - 系统会自动应用
view-transition-group: contain范围,以启用嵌套视图过渡组。 - 如果范围根剪裁其溢出内容,则生成的
::view-transition-group-children(root)伪元素会自动使用overflow: clip剪裁其内容,从而防止伪元素在视觉上从过渡根中溢出。
这样一来,您就可以只关注要捕获的元素,而无需考虑元素范围内的视图转换所使用的 CSS。例如,在列表演示中,CSS 仅向列表项添加名称:
ul li {
view-transition-name: match-element;
view-transition-class: album;
}
在以下演示中,您可以试用一下。您可以选择不参与自己的活动。如果范围是自行参与(默认行为),一切都会按预期运行。当范围不是自参与时,其边框会立即发生变化,并且其内容会在过渡期间溢出包装容器。
实时演示
演示录制
供参考,此演示的伪树(包含自我参与)如下所示:
html
├─ head
└─ body
├─ ul
│ ├─ ::view-transition
│ │ └─ ::view-transition-group(root)
│ │ ├─ ::view-transition-group-children(root)
│ │ │ ├─ ::view-transition-group(item1)
│ │ │ │ └─ ::view-transition-image-pair(item1)
│ │ │ │ ├─ ::view-transition-old(item1)
│ │ │ │ └─ ::view-transition-new(item1)
│ │ │ ├─ ::view-transition-group(item2)
│ │ │ │ └─ …
│ │ │ …
│ │ └─ ::view-transition-image-pair(root)
│ │ ├─ ::view-transition-old(root)
│ │ └─ ::view-transition-new(root)
│ ├─ li
│ ├─ li
│ └─ li
└─ button#reorder
由于过渡根元素 <ul> 会垂直剪裁其内容,因此 ::view-transition-group-children(root) 也会自动应用剪裁。
并发元素范围视图转换
由于元素范围的视图转换是单独运行的,因此如果多个元素范围的视图转换具有不同的范围,则可以同时运行。
以下演示包含两个重新排序按钮,每个列表对应一个按钮。每个按钮仅在其各自的列表中启动元素范围的视图转换。由于这两个列表的 DOM 树不重叠,因此这两个元素范围的视图转换可以同时独立运行。
实时演示
演示录制
这种隔离特性还允许您在不同范围内重复使用 view-transition-name 值。只要名称在其范围内保持唯一性,就不会发生冲突。
嵌套的元素范围视图转换和 view-transition-name 包含
当多个元素范围视图转换的 DOM 树重叠时,存在 view-transition-name 值冲突的风险。因此,浏览器会自动为有效的元素范围视图过渡分配 view-transition-scope: all,以降低此风险。
与 anchor-scope 范围 anchor-name 值类似,view-transition-scope 属性可确保 view-transition-name 值限定在元素的子树中。该属性接受 none(您要限定范围的名称列表)或 all(限定所有值的范围)。
除了防止名称溢出之外,view-transition-scope 还可以防止元素及其内容被外部并发视图转换捕获。当快照拍摄过程遍历子树以查找要拍摄快照的元素时,它会忽略应用了 view-transition-scope: all 的元素(及其整个子树)。这假设这些元素已参与到其他元素范围的视图过渡中。
以下演示是上一个演示的变体。除了用于随机播放列表内容的两个按钮之外,它还有一个用于交换列表的交换按钮。在 #lists-wrapper 上切换 .reversed 类会处理交换。
实时演示
演示录制
由于 view-transition-scope: all 会在随机播放过渡期间自动应用,因此您可以在随机播放过渡仍在进行时启动并发的外部交换过渡。
由于 view-transition-scope: all 还会阻止元素在外部过渡中被拍摄快照,因此演示还向封装 <ul> 元素的元素添加了 view-transition-name 值。
#list1-wrapper, #list2-wrapper {
view-transition-name: attr(id type(<custom-ident>));
}
在此演示中,在对第二个列表开始随机排序,然后交换两个列表后,伪树如下所示:
html
├─ head
└─ body
└─ #lists-wrapper.reversed (SCOPE)
├─ ::view-transition
│ └─ ::view-transition-group(lists-wrapper)
│ ├─ ::view-transition-group-children(lists-wrapper)
│ │ ├─ ::view-transition-group(list1-wrapper)
│ │ │ └─ ::view-transition-image-pair(list1-wrapper)
│ │ │ ├─ ::view-transition-old(list1-wrapper)
│ │ │ └─ ::view-transition-new(list1-wrapper)
│ │ └─ ::view-transition-group(list2-wrapper)
│ │ └─ ::view-transition-image-pair(list2-wrapper)
│ │ ├─ ::view-transition-old(list2-wrapper)
│ │ └─ ::view-transition-new(list2-wrapper)
│ └─ ::view-transition-image-pair(lists-wrapper)
│ ├─ ::view-transition-old(lists-wrapper)
│ └─ ::view-transition-new(lists-wrapper)
├─ div#list1-wrapper
│ ├─ ul
│ │ ├─ li#item1
│ │ ├─ li#item2
│ │ └─ li#item3
│ └─ button.reorder
└─ div#list2-wrapper
├─ ul (SCOPE)
│ ├─ ::view-transition
│ │ └─ ::view-transition-group(list)
│ │ ├─ ::view-transition-group-children(list )
│ │ │ ├─ ::view-transition-group(item4)
│ │ │ │ └─ ::view-transition-image-pair(item4)
│ │ │ │ ├─ ::view-transition-old(item4)
│ │ │ │ └─ ::view-transition-new(item4)
│ │ │ ├─ ::view-transition-group(item5)
│ │ │ │ └─ …
│ │ │ …
│ │ └─ ::view-transition-image-pair(list)
│ │ ├─ ::view-transition-old(list)
│ │ └─ ::view-transition-new(list)
│ ├─ li#item4
│ ├─ li#item5
│ └─ li#item6
└─ button.reorder
了解详情
如需详细了解元素范围的视图过渡,请参阅说明文档、css-view-transitions-2 规范和未完成的规范编辑列表。