单页应用的视图过渡

网页的一种常见模式是使用 JavaScript 动态替换网页上的内容,而无需加载新的完整 HTML 文档。这称为单页应用 (SPA)。视图过渡功能可让您在 SPA 中的网页之间显示连续性或上下文。

全屏过渡

当用户在 SPA 中导航到新视图时,框架会使用新内容替换 DOM。这样一来,内容就会直接显示,但如果您想在当前内容和新内容之间提供过渡效果,该怎么办?

过渡效果通常会同时显示旧视图和新视图,例如淡出旧视图,同时淡入新视图。由于现有内容会被替换,因此在视图过渡之前,这是一个难题。

如需使用视图过渡,您需要将用于更改 DOM 的逻辑封装在回调中。在这些示例中,我们通过名为 MyRouter 的 Web 组件提供了一个基本路由器实现。启用视图过渡的方式取决于您使用的路由器和框架。

document.startViewTransition(() => updateTheDOMSomehow());

这会启用默认过渡效果,即旧视图淡出,同时新视图淡入。

这里发生了什么?当您调用 document.startViewTransition() 时,浏览器会拍摄旧视图的快照。然后,它会调用您传递的回调函数,该函数会将 DOM 更新为新视图(但尚未显示)。回调函数完成后,浏览器会开始过渡到新内容。

// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
  updateTheDOMSomehow();
  return;
} else {
  // With a View Transition:
  document.startViewTransition(() => updateTheDOMSomehow());
}

自定义过渡效果

如您在前面的示例中所见,默认的视图过渡效果是淡出旧视图,同时淡入新视图。您可以通过设置由视图转换生成的伪元素的样式,自定义过渡效果,使其更符合网站的风格。

您可以使用 ::view-transition-old() 指定离开过渡效果,并使用 ::view-transition-new() 指定进入过渡效果。您还可以使用 ::view-transition-group() 同时指定这两个值。

在此示例中,旧视图将使用 slide-out-to-left 过渡效果淡出,新视图将使用 slide-in-from-right 过渡效果淡入。两者的时长均为 200 毫秒。

::view-transition-group(root){
  animation-duration: 200ms;
}

::view-transition-old(root) {
  animation-name: slide-out-to-left;
}

::view-transition-new(root) {
  animation-name: slide-in-from-right;
}

根据情景采用不同的过渡效果

您可能希望根据用户正在执行的操作来设置不同的过渡效果。例如,如果点击首页上的链接会使新视图从右侧滑入,那么您会预期点击链接返回首页时,首页视图会从左侧滑入。

您可以使用 :active-view-transition-type() 伪类指定不同的动画。

html:active-view-transition-type(forwards) {
  &::view-transition-old(root) {
    animation-name: slide-out-to-left;
  }

  &::view-transition-new(root) {
    animation-name: slide-in-from-right;
  }
}

然后,您可以选择在调用 document.startViewTransition() 时使用哪种视图过渡类型。

const direction = next === 'home' ? 'backwards' : 'forwards';

document.startViewTransition({
  update: updateTheDOMSomehow,
  types: [direction],
});

过渡特定元素

到目前为止,您只将过渡效果应用于根元素,以过渡整个视图。不过,您也可以使用视图过渡为网页的特定部分添加动画效果。

例如,旧视图中的内容可能与新视图中的内容相匹配。可以是内容标题或图片。旧视图中甚至可以是缩略图,而新视图中是视频。

首先,您需要使用 view-transition-name 属性指定要过渡的元素。为了使视图转换正常运行,对于每个 view-transition-name,在调用 document.startViewTransition() 之前需要恰好有一个元素,并且在 document.startViewTransition() 中的回调完成之后也需要恰好有一个元素。

在此示例中,音乐播放器会显示专辑封面、标题和音乐人。另一种视图显示的是重新排列的相同内容,并添加了歌词。

在上面的示例中,旧视图和新视图中各有且仅有一个过渡元素,并且它们甚至共享相同的选择器。过渡元素看起来会在其大小和位置之间移动。视图中未发生过渡的部分会淡入和淡出。

我们来看一个更复杂的示例。例如,博客首页可能会显示每篇博文的标题和图片,这些内容也会显示在博文的完整网页视图中。在从首页导航到特定帖子时,您可能希望让标题和图片看起来像是过渡到新位置,以提供上下文。

若要为标题执行此操作,您需要在旧视图中的标题元素上设置一个唯一的 view-transition-name,该 view-transition-name 与新视图中的标题元素共享,并且在新视图中是唯一的。这是一个难题,因为首页包含多个标题和图片,您不知道用户会点击哪个。

您可以通过两种方式解决此问题。您可以选择为首页上的每篇帖子添加唯一的 view-transition-name,然后在每篇完整版帖子中匹配该名称。您可以使用帖子的 ID 生成这些内容。另一种选择是使用通用 view-transition-name,但仅在用户点击帖子后、调用 document.startViewTransition() 之前应用。

设计过渡

视图过渡是一组工具,可用于引导用户并提供额外的品牌或上下文提示。您可能会使用多种技术来找到适合您网站的过渡效果。

根据您想要的效果,您可能还需要调整元素或动画。在前面的示例中,我们调整了多种样式,以实现平滑的过渡效果。

标题具有规则 width: fit-content,当您转换不换行的文字(或在旧视图和新视图中具有相同的换行)时,这是一种有用的样式。否则,过渡可能发生在宽度不同的元素之间,这会使过渡效果不够平滑。

此外,图片在旧视图和新视图中的宽高比也不同。此示例修改了动画和 object-fit 属性,使过渡看起来很平滑。

尊重 prefers-reduced-motion

用户请求减少动态效果的一个常见原因是,全屏动画(例如通过视图过渡实现的全屏动画)可能会让患有平衡障碍的人感到不适。您可以使用 prefers-reduced-motion 媒体查询停用动画。您还可以选择提供更细致的替代动画,但仍能传达元素之间的关联。

@media (prefers-reduced-motion) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

检验您的掌握情况

表示调用 document.startViewTransition() 之前的视图的伪元素的名称是什么?

::view-transition-previous
错误。
::view-transition-prior
错误。
::view-transition-old
正确!
::view-transition-initial
错误。

视图过渡的默认动画是什么?

淡出旧内容,淡入新内容
正确!
从左向右滑动
错误。
旧内容淡出变白,淡入新内容
错误。
星形擦除
错误。

网页的默认 view-transition-name 是什么?

document
错误。
shadow-root
错误。
root
正确!
body
错误。