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