使用 View Transitions API 更简单地实现动画

使用 View Transitions API 可以轻松创建不同 DOM 状态之间的转换动画,避免了编写大量的 CSS 和 JavaScript 来处理新旧内容的共存逻辑。

2024-05-11
2026-10-29

View Transitions API 是一种用于创建动画的浏览器 API,常用于 SPA(单页面应用)中的过渡切换动画。使用 View Transitions API 可以轻松创建不同 DOM 状态之间的转换动画,避免了编写大量的 CSS 和 JavaScript 来处理新旧内容的共存逻辑。

这是一个实验性功能,仅在 Chrome 和 Edge 的 111 及以上版本可用,Firefox 和 Safari 暂时不可用。

View Transitions API 的工作原理

View Transitions API 的工作原理简单来说,就是在更新 DOM 前后,截取旧页面视图和新页面视图,在过渡动画执行过程中覆盖原有页面,显示在最上层。有了新旧视图,我们就可以使用 CSS 或者 JavaScript 来创建任意的过渡动画。

让我们来看看它是如何工作的:

创建 View Transitions

document.startViewTransition(callback) 函数启动一个新的 View Transitions,并返回一个 ViewTransition 对象来表示它。

API 会截取当前页面的屏幕截图作为旧页面视图,是静态图像。

其中该函数的参数 callback 是 回调函数。

调用回调函数更新 DOM

API 会调用传入的回调函数,用于在视图转换过程中更新 DOM。

在函数执行完成后,会实现 Promise ViewTransition.updateCallbackDone。

API 会获取当前的新页面视图,并且与现在的实际页面保持同步。

创建伪元素树

::view-transition
└─ ::view-transition-group(root)
   └─ ::view-transition-image-pair(root)
      ├─ ::view-transition-old(root)
      └─ ::view-transition-new(root)
  • ::view-transition-old(root) 是旧页面视图
  • ::view-transition-new(root) 是新页面视图

通过修改伪元素树来实现动画

在动画执行前,会实现 Promise ViewTransition.ready。可以在这里通过 JavaScript 创建自定义动画。

除此以外还可以通过覆盖原有 CSS 来自定义动画。默认 CSS 动画为:旧页面视图不透明度从 1 到 0,而新页面视图不透明度从 0 到 1,持续时间 0.25 秒。

在动画执行后,会实现 Promise ViewTransition.finished。

View Transitions API 的用法

基于上述工作原理,我们来使用 View Transitions API。

基本使用

使用 document.startViewTransition() 函数来启动一个新的 View Transitions,并传入一个回调函数用于修改 DOM。

const transition = document.startViewTransition(() => changeDOM());

这样就实现了一个使用默认过渡动画进行过渡例子。

使用 CSS 自定义过渡动画

除了默认动画外,可以使用 CSS 通过修改伪元素来自定义过渡动画。例如修改动画时间、延迟、动画名称等:

::view-transition-old(root),
::view-transition-new(root) {
  animation: 0.25s 0.25s linear both myanimation;
}

使用 JS 自定义过渡动画

我们还可以使用 JS 来实现更加复杂的过渡动画。

在 Promise ViewTransition.ready 实现后,使用 document.documentElement.animate 等方式来实现动画:

const transition = document.startViewTransition(() => changeDOM());

transition.ready.then(() => {
  document.documentElement.animate(...); // 创建自己的动画效果
});

使用 JS 自定义过渡动画需要覆盖默认的 CSS 动画:

::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
}

不同元素使用不同过渡动画

如果希望不同的元素的动画与默认的 root 动画不同,可以使用 view-transition-name 属性将它们分开。

.myel {
  view-transition-name: myel;
}

::view-transition-old(myel),
::view-transition-new(myel) {
  
}

考虑不支持 View Transitions 的浏览器

可以通过 document.startViewTransition 是否存在来判断浏览器是否支持 View Transitions,在不支持 View Transitions 的浏览器上直接修改 DOM,在支持 View Transitions 的浏览器上将修改 DOM 的函数作为 startViewTransition 参数即可。

// 不支持 View Transitions 的浏览器
if (!document.startViewTransition) {
    changeDOM();
    return;
}

// 支持 View Transitions 的浏览器
const transition = document.startViewTransition(() => changeDOM());

示例应用:圆形放大过渡动画

假设有一个函数 switch_appearance() 可以切换页面的外观,那么可以通过 View Transitions API 实现切换动画。下面我们编写一个新的外观切换函数 switch_appearance_with_transitions(),用来实现圆形放大过渡动画:

// 这个函数是点击事件的回调函数,带有参数 event
function switch_appearance_with_transitions(event) {
  const transition = document.startViewTransition(() => {
    switch_appearance()
  });

  // 获取点击位置,作为圆心,根据页面大小计算半径
  const x = event.clientX;
  const y = event.clientY;
  const endRadius = Math.hypot(
    Math.max(x, innerWidth - x),
    Math.max(y, innerHeight - y)
  );

  // 等待伪元素创建完成:
  transition.ready.then(() => {
    // 新视图的根元素动画:构造一个圆形作为裁切,并且半径会从0开始放大到endRadius
    document.documentElement.animate(
      {
        clipPath: [
          `circle(0 at ${x}px ${y}px)`,
          `circle(${endRadius}px at ${x}px ${y}px)`,
        ],
      },
      {
        duration: 500,
        easing: "ease-in",
        // 指定要附加动画的伪元素
        pseudoElement: "::view-transition-new(root)",
      },
    );
  });
}

还需要添加以下 CSS 清除默认动画,并且保持新页面显示在上层:

::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}

::view-transition-old(root) {
  z-index: 1;
}
::view-transition-new(root) {
  z-index: 999;
}

实际使用时需要注意考虑不支持 View Transitions 的浏览器。

参考文献

https://developer.mozilla.org/zh-CN/docs/Web/API/View_Transitions_API

文章目录

版权

本文遵循署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0) 标准。

部分内容源自互联网、书本和课堂,仅供交流学习,未用于任何商业用途。如果它们侵犯了您的权益,请与我们联系,我们将立即删除它们。

版权所有 © 2020-2026 Kloudy 保留所有权利.