Get to know MDN better
此页面由社区从英文翻译而来。了解更多并加入 MDN Web Docs 社区。
本文介绍了视图过渡 API 的工作原理,如何创建视图过渡,如何自定义过渡动画,以及如何操作活动状态的视图过渡。这涵盖了单页应用程序(SPA)中 DOM 状态更新的视图过渡,以及在多页应用程序(MPA)中的文档之间的导航。
让我们来了解一下视图过渡的工作原理:
视图过渡被触发。它如何执行取决于视图过渡的类型:
备注:活动的视图过渡具有关联的 ViewTransition 实例(例如,在同文档(SPA)过渡的情况下,由 startViewTransition() 返回)。ViewTransition 对象包含多个 Promise,允许你运行代码以响应到达视图过渡过程的不同部分。有关更多信息,请参阅使用 JavaScript 控制视图过渡。
在当前(旧页面)视图上,API 捕获声明了 view-transition-name 的元素的快照。
视图更改发生:
对于同文档(SPA)过渡,将调用传递给 startViewTransition() 的回调,这会导致 DOM 发生更改。
当回调成功运行时,ViewTransition.updateCallbackDone promise 将兑现,允许你响应 DOM 更新。
在跨文档(MPA)过渡的情况下,导航发生在当前文档和目标文档之间。
API 将新视图中的快照捕获为实时表示的形式。
此时,视图过渡即将运行,并且 ViewTransition.ready Promise 兑现。例如,允许你通过运行自定义 JavaScript 动画而不是默认动画来响应。
旧页面快照以“淡出”动画形式显示,而新视图快照以“淡入”形式呈现动画效果。默认情况下,旧视图快照的动画效果是 opacity 属性值从 1 到 0,而新视图快照的动画效果是 opacity 属性值从 0 到 1,这会创建一个交叉淡化。
当过渡动画达到其结束状态时,ViewTransition.finished Promise 兑现,从而允许你做出响应。
备注:如果在 document.startViewTransition() 调用期间,文档的页面可见性状态处于 hidden 状态(例如,如果文档被窗口遮挡、浏览器最小化或另一个浏览器选项卡处于活动状态),则会完全跳过视图过渡。
为了处理传出和传入过渡动画的创建,此 API 构造了一个具有以下结构的伪元素树:
::view-transition └─ ::view-transition-group(root) └─ ::view-transition-image-pair(root) ├─ ::view-transition-old(root) └─ ::view-transition-new(root)备注:每个被捕获的 view-transition-name 都会创建一个 ::view-transition-group 子树。
对于同文档(SPA)过渡,伪元素树在当前文档中可用。对于跨文档过渡(MPA),伪元素树仅在目标文档中可用。
树结构中最有趣的部分如下:
::view-transition 是视图过渡遮罩层的根伪元素,它包含所有视图过渡快照组,并位于所有其他页面内容的顶部。
::view-transition-group 充当每个视图过渡快照组的容器。root 参数指定默认快照组——视图转换动画将应用于 view-transition-name 为 root 的快照。默认情况下,它是 :root 元素,因为默认的浏览器样式定义了这个:
但请注意,网页的作者可以通过取消设置上述内容并在其他元素上设置 view-transition-name: root 来更改此设置。
::view-transition-old 指向旧的页面元素的静态快照,而 ::view-transition-new 指向新的页面元素的实时快照。这两个选项都以与 <img> 或 <video> 相同的方式呈现为替换内容,这意味着它们可以使用方便的属性来设置样式,如 object-fit 和 object-position。
备注:可以通过在每个元素上设置不同的 view-transition-name 来使用不同的自定义视图过渡动画指向不同的 DOM 元素。在这种情况下,会为每个元素创建一个 ::view-transition-group。有关示例,请参见不同元素的不同动画。
备注:正如你稍后将看到的,要自定义传出和传入动画,你需要将动画分别指向 ::view-transition-old 和 ::view-transition-new 伪元素。
本节说明如何在 SPA 和 MPA 情况下创建基本视图过渡。
例如,SPA 可能包含获取新内容和更新 DOM 以响应某种事件的功能,例如单击导航链接或从服务器推送更新。在我们的视图过渡 SPA 演示中,我们已将其简化为 displayNewImage() 函数,该函数根据单击的缩略图显示新的全尺寸图像。我们将其封装在一个 updateView() 函数中,该函数仅在浏览器支持时调用视图过渡 API:
此代码足以处理显示图像之间的过渡。支持的浏览器会将从旧图像和标题到新图像和标题的更改显示为平滑的交叉淡化(即默认视图过渡)。它仍然可以在不支持的浏览器中工作,但没有漂亮的动画。
创建跨文档(MPA)视图过渡时,该过程甚至比 SPA 更简单。因为视图更新是由跨文档、同源导航触发的,而不是由 JavaScript 引发的 DOM 更改触发的,所以不需要 JavaScript。要启用基本的 MPA 视图过渡,你需要在 CSS 中为当前文档和目标文档指定 @view-transition at 规则以选择启用,如下所示:
我们的视图过渡 MPA 演示展示了这个规则的实际应用,并还演示了如何自定义传出和传入动画的视图过渡。
备注:目前,只能在同源文档之间创建 MPA 视图过渡,但在将来的实施中可能会放宽此限制。
视图过渡伪元素应用了默认的 CSS 动画(详见其参考页面)。
如上所述,大多数外观过渡都带有默认的平滑交叉淡化动画。有一些例外情况:
你可以使用常规 CSS 以任何你想要的方式修改默认动画——使用 ::view-transition-old 定位“来源”动画,使用 ::view-transition-new 定位“目标”动画。
例如,要更改两者的速度:
建议你将这样的样式定位到 ::view-transition-group(),以将它们应用于 ::view-transition-old() 和 ::view-transition-new()。由于伪元素层次结构和默认用户代理样式,样式将被两者继承。例如:
备注:这也是保护代码的好选择——::view-transition-group() 也可以动画化,并且 group/image-pair 伪元素与 old 和 new 伪元素的持续时间可能会有所不同。
在跨文档(MPA)过渡的情况下,伪元素需要包含在目标文档中,视图过渡才能正常工作。如果你想在两个方向上使用视图过渡,你当然需要在两个方向上都包含它。
我们的视图过渡 MPA 演示包括上述 CSS,但更进一步,定义了自定义动画并将它们应用于 ::view-transition-old(root) 和 ::view-transition-new(root) 伪元素。结果是,在进行导航时,默认的交叉淡化过渡被替换成了“向上滑动”过渡:
默认情况下,在视图更新期间更改的所有不同元素都使用相同的动画进行过渡。如果你希望某些元素的动画效果与默认的 root 动画不同,你可以使用 view-transition-name 属性将它们分开。例如,在我们的视图过渡 SPA 演示中,<figcaption> 元素被赋予了 figure-caption 的 view-transition-name,以便在视图过渡方面将它们与页面的其余部分分开:
应用此 CSS 后,生成的伪元素树现在将如下所示:
::view-transition ├─ ::view-transition-group(root) │ └─ ::view-transition-image-pair(root) │ ├─ ::view-transition-old(root) │ └─ ::view-transition-new(root) └─ ::view-transition-group(figure-caption) └─ ::view-transition-image-pair(figure-caption) ├─ ::view-transition-old(figure-caption) └─ ::view-transition-new(figure-caption)第二组伪元素的存在允许将单独的视图过渡样式仅应用于 <figcaption> 元素。不同的旧视图捕获和新视图捕获彼此分开处理。
备注:view-transition-name 的值可以是你想要的任何值,除了 none 以外——none 值明确表示元素不会参与视图过渡。
view-transition-name 值也必须是唯一的。如果两个渲染的元素同时具有相同的 view-transition-name,ViewTransition.ready 将拒绝并跳过过渡。
以下代码仅将自定义动画应用于 <figcaption>:
在这里,我们创建了一个自定义的 CSS 动画,并将其应用于 ::view-transition-old(figure-caption) 和 ::view-transition-new(figure-caption) 伪元素。我们还为这两个样式添加了许多其他样式,以将它们保持在同一个位置,并防止默认样式干扰我们的自定义动画。
备注:你可以使用 * 作为伪元素中的标识符,以定位所有快照伪元素,无论它们的名称如何。例如:
请注意,我们还发现了另一个过渡选项,它比上述选项更简单,并且产生了更好的结果。我们最终的 <figcaption> 视图过渡最终看起来像这样:
这之所以有效,是因为默认情况下,::view-transition-group 以平滑的比例在新旧视图之间转换 width 和 height。我们只需要在这两个状态上设置一个固定的 height 来使其正常工作。
备注:使用视图过渡 API 实现平滑过渡包含其他几个自定义示例。
视图过渡有一个关联的 ViewTransition 对象实例,该实例包含多个 promise 成员,允许你运行 JavaScript 以响应所达到的过渡的不同状态。例如,ViewTransition.ready 在创建伪元素树且动画即将开始时兑现,而 ViewTransition.finished 在动画完成后兑现,并且新的页面视图对用户可见且具有交互性。
可以像这样访问 ViewTransition:
备注:如果导航在重定向链中的任意位置具有跨源 URL,则 activation 属性返回 null。
让我们看一些示例代码来展示如何使用这些功能。
以下 JavaScript 可用于创建从用户光标位置发出的循环显示的视图过渡,动画由 Web 动画 API 提供。
此动画还需要以下 CSS 来关闭默认的 CSS 动画并阻止新旧视图状态以任何方式混合(新状态会在旧状态的顶部“擦除”,而不是过渡):
Chrome DevRel 团队成员列表演示提供了一组基本的团队配置文件页面,并演示了如何使用 pageswap 和 pagereveal 事件来自定义基于“来源”和“目标”URL 的跨文档视图过渡的传出和传入动画。
pageswap 事件侦听器如下所示。这将在出站页面上链接到用户档案页面的元素上设置视图过渡名称。从主页导航到配置文件页面时,仅为在每种情况下单击的链接元素提供自定义动画。
备注:在每种情况下,我们在拍摄快照后删除 view-transition-name 值。如果我们设置它们,它们将在导航时保留在 bfcache 中保存的页面状态中。如果随后按下后退按钮,则被导航回的页面的 pagereveal 事件处理器将尝试在不同的元素上设置相同的 view-transition-name 值。如果多个元素设置了相同的 view-transition-name ,则跳过视图过渡。
pagereveal 事件侦听器如下所示。这与 pageswap 事件侦听器的工作方式类似,但请记住,这里我们为新页面上的页面元素自定义“目标”动画。
在运行跨文档过渡之前,你最好等到页面状态稳定下来,依靠渲染阻塞来确保:
默认情况下,样式是被渲染阻塞的,并且脚本可以通过使用 blocking="render" 属性来被渲染阻塞。
要确保初始 HTML 已解析并在过渡动画运行之前始终一致地呈现,你可以使用 <link rel="expect">。在此元素中,你将包括以下属性:
让我们通过一个示例 HTML 文档来探讨一下它是什么样子的:
结果是,在解析潜在内容 <div> 之前,文档渲染会被阻止,从而确保视图过渡的一致性。
你还可以在 <link rel="expect"> 元素上指定 media 属性。例如,在窄屏幕设备上加载页面时,你可能希望阻止在比在宽屏设备上加载页面时少的内容上呈现。这是有道理的——在移动设备上,页面首次加载时可见的内容比在桌面上要少。
这可以通过以下 HTML 来实现: