本文作者系360奇舞团前端开发工程师
Web 动画不仅能够提升用户界面的美观度,还能增强用户的交互体验。
在手机app中,经常会见到这种丝滑的交互动画,用来引导用户的视线,使用户更加关注产品的重要信息。这种交互动画在安卓端被称为共享元素动画。通过观察可以发现这种动画的特点,就是在动画前后有一个或者多个相同的元素。
那么在浏览器里前端如何实现这样的动画呢?
通常都会在触发动画的时候新建一个dom结构,计算动画元素的初始状态和最终状态,通过js的animate执行动画。也就是FLIP的思想来实现。这种实现的存在的问题是需要关注有哪些元素参与了动画,动画前后有哪些属性发生了变化,手动计算并应用到元素上。其次还需要保证元素是始终存在页面中,并且还不能实现跨页面的动画过渡。
View Transition
view transition是w3c工作组提出的一个草案,view transition允许你在可视 DOM 改变的两种状态之间添加动画过渡。这些改变的范围可以从向 DOM 添加新元素这样的小更改,到从一个页面导航到另一个页面这样的的大更改。view transition提供了一种简单的方式来实现dom的更改和动画。
目前的浏览器兼容性如下图:
使用view transition可以非常简单的实现共享元素动画,那么接下来看一下view transition的相关内容吧!
View Transition的核心概念
view transition提供了一个js方法:
document.startViewTransition(updateCallback);
通过这个方法可以开启view transition。其中startViewTransition方法接收一个回调函数作为参数,在回调函数updateCallback 里,我们执行变更 DOM 状态的逻辑,比如切换页面路由、更新页面内容等。
下图是没有使用view transiton和使用了view transtion的对比:
在调用了startViewTransition后会得到一个只读的promise对象,这个对象有三个属性,代表了过渡动画中的三个状态节点:
1. updateCallbackDone: 回调函数执行完毕。
2. ready:准备完毕即将准备播放动效。
3. finished:动效执行结束。
这三个属性也都是promise对象,可以使开发者更精准的控制动画的过程。
const withTransitionButton = document.querySelector('.with-transition button');
withTransitionButton.addEventListener("click", async (e) => {
const vt = document.startViewTransition(() => {
const newImage = document.createElement("img");
newImage.src = "//p5.img.360kuai.com/t01ab608eed52439d4a.jpg";
e.target.parentElement.appendChild(newImage);
console.log('Update Callback')
});
console.log('View Transition Instance', vt);
// vt.skipTransition();
// updateCallback函数执行状态 会在 updateCallback函数执行完时兑现
await vt.updateCallbackDone;
console.time('Callback Done');
// 跳过快照动画,但不会跳过updateCallback函数执行
vt.skipTransition();
// 动画是否准备好 会在伪元素树被创建且过渡动画即将开始时兑现
await vt.ready
// console.log('Transition Ready');
// vt.skipTransition();
// 转场动画是否执行完 会在转场过渡动画完成时兑现
await vt.finished
console.log('Transition Finished');
});
返回的promise对象还有一个skipTransition方法,可以跳过动画效果而不影响updatecallback函数的执行。
View Transition背后的运行机制
view transition的API设计非常简单,那么浏览器是如何实现这种过渡动画的呢?
当调用document.startViewTransition方法的时候,浏览器会捕获当前的页面状态作为旧视图,暂停浏览器的渲染,执行updatecallback函数,更新dom,这个时候虽然dom更新了,但是并没有渲染,所以用户看的内容并没有变化。浏览器会再次捕获到当前dom的状态作为新视图。updatecallback执行完后,updateCallbackDone的状态变更。
浏览器会利用刚刚生成的新旧视图,创建一系列伪元素,插入到 html 节点中。旧、新视图,对应着上文提到的两个伪元素:::view-transition-old、::view-transition-new。
之后再恢复渲染,这时用户会看到伪元素渲染的样式,此时的ready状态完成。
浏览器会对这些伪元素执行动画。伪元素的层级是最高的,所以在执行动画的过程中,页面不能进行任何的交互。
动画结束后,添加到页面的伪元素会被浏览器移除。finished状态完成。
(在 W3C 标准文档中,有一个步进式 Demo,可以帮我们更直观地了解生命周期的每个阶段)。
View Transition的视图命明和自定义动画
在核心概念里的demo可以看到view transition的动画是交叉淡化动画,这个是view transition的默认动画。在运行机制里我们了解到view transition是通过伪元素来实现动画的,所以也就支持通过css来自定义动画。
以下面的元素点击后变小删除的demo来看一下view transition如何自定义动画。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box{
width: 100px;
height: 100px;
background: red;
view-transition-name: box;
border-radius: 50%;
}
@keyframes scale-small {
from {
transform: scale(1);
}
to {
transform: scale(0);
}
}
::view-transition-old(box) {
animation: scale-small 2s forwards;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
document.getElementById('box').addEventListener('click', (e) => {
document.startViewTransition(() => {
e.target.remove()
})
})
</script>
</body>
</html>
为了保证动画只作用到删除元素,需要给元素定一个view-transition-name的css属性来命名视图转换。之后就可以用::view-transition-old(box)选择到这个元素的旧状态,并对其设置动画。
当一个页面中有多个命名视图的话,就会产生多组的伪元素结构:
其中视图转换不要求在同一个元素上,可以是不同的元素,但要保证在一个页面中是唯一的。
View Transition的多页面
到目前为止,过渡效果只能在同一文档中实现,但是view transition它允许你在跨不同文档导航时添加过渡。换句话说,你还可以向多页面应用程序添加过渡效果。
要启用这些页面之间的视图转换,你需要做的就是将以下 标记添加到文档的 HTML 中:
<meta name="view-transition" content="same-origin">
可以通过这个demo(https://view-transition-demo-crossfade.netlify.app/)来查看效果(在使用chrome的时候需要浏览器版本>111 并且手动启用了view transition的api)
开源框架对View Transition的支持
- Astro v2.9 (https://astro.build/blog/astro-290/)
- SvelteKit 从1.24 版本,集成了View Transition API (https://svelte.dev/blog/ view-transitions)
- Nuxt.js v3.4 将view transition作为实验性功能引入 (https://nuxt.com/blog/v3-4#view-transitions-api-support)
- React-router v6.17.0 (https://github.com/remix-run/react-router/blob/main/ CHANGELOG.md#view-transitions-)
总结
创建平滑的页面过渡一直是 Web 开发的目标。View Transitions API 是实现这一目标的一大进步。它允许开发人员在网站上添加有吸引力的动画。
- END -