主题过渡动画
刀刀
0字
0分钟
2026/8/16
Element UI 切换主题时有一个很炫酷的过渡动画,这主要是通过 view transition 实现的。
CSS view transition
view transition 是一个 CSS 规范,用于在页面切换时添加过渡动画。它包含两个部分:
view transition规则:用于定义过渡动画的属性,如transition-property、transition-duration等。view transition事件:用于监听过渡动画的开始和结束,从而执行相应的操作。
它的工作原理如下:
调用
document.startViewTransition()方法时,会截取当前页面的屏幕截图接着调用回调函数,在回调函数中修改 DOM 结构
API 捕获页面的新状态并实时展示
API 构造一个具有以下结构的伪元素树
js::view-transition └── ::view-transition-group(root) └── ::view-transition-image-pair(root) ├── ::view-transition-old(root) └── ::view-transition-new(root)
CSS 代码实现
CSS 基础实现
写一个切换主题色的代码:
html
<style>
:root {
--background-color: #fff;
background-color: var(--background-color);
}
:root.dark {
--background-color: #000;
}
</style>
<body>
<button id="btn">切换主题</button>
<script>
const btn = document.getElementById('btn')
btn.addEventListener('click', () => {
document.documentElement.classList.toggle('dark')
})
</script>
</body>CSS 过渡实现
使用 view transition 实现过渡动画,步骤:
document.startViewTransition()方法开始过渡动画,接收一个函数作为参数,该函数用于修改 DOM 结构(即修改className部分),得到一个参数,命名为transition- 在
transition.ready动画捕获时,添加过渡动画的样式。由于这是一个异步方法,因此需要在.then回调中执行 - 调用
document.documentElement.animate方法设置动画,参数一是一个对象,属性名是需要实现的动画的,值是一个数组,第一项是起始样式,第二项是结束样式;参数二是一个对象,包含duration过渡时间、pseudoElement过渡元素等属性
动画过渡本例采用 clip-path 属性,它用于裁剪元素,使元素的部分内容不可见。设置一个圆形,从 0% 到 100% 的变化,从而实现一个圆形的放大效果。
html
<style>
:root {
--background-color: #fff;
background-color: var(--background-color);
}
:root.dark {
--background-color: #000;
}
::view-transition-new(root),
::view-transition-old(root) {
animation: none !important;
}
</style>
<body>
<button id="btn">切换主题</button>
<script>
const btn = document.getElementById('btn')
btn.addEventListener('click', () => {
const transition = document.startViewTransition(() => {
document.documentElement.classList.toggle('dark')
})
transition.ready.then(() => {
document.documentElement.animate(
{
clipPath: [`circle(0% at 50% 50%)`, `circle(100% at 50% 50%)`],
},
{
duration: 500,
pseudoElement: '::view-transition-new',
},
)
})
})
</script>
</body>运行代码,实现了从中心圆形扩散的过渡动画。后面调整细节。
注意
如果没有动画效果,可能是原来的默认动画样式覆盖了设置的动画样式,需要在 ::view-transition-new 和 ::view-transition-old 中添加 animation: none !important;。
CSS 优化细节
还有几点细节需调整:
- 修改中心点为鼠标点击的位置
clip-path的circle半径要计算修改,否则中心点在四个角时,会因为半径不够,导致后续没有动画
js
const btn = document.getElementById('btn')
btn.addEventListener('click', (e) => {
const transition = document.startViewTransition(() => {
document.documentElement.classList.toggle('dark')
})
const x = e.clientX
const y = e.clientY
const targetRadius = Math.hypot(
Math.max(x, window.innerWidth - x),
Math.max(y, window.innerHeight - y),
)
transition.ready.then(() => {
document.documentElement.animate(
{
clipPath: [
`circle(0% at ${x}px ${y}px)`,
`circle(${targetRadius}px at ${x}px ${y}px)`,
],
},
{
duration: 500,
pseudoElement: '::view-transition-new',
},
)
})
})