前言
最近在研究网页主题切换的动画效果时,我发现了一个非常实用的 Web API —— View Transition API,它可以让网页的主题切换变得丝滑流畅,给用户带来更佳的视觉体验。今天,就来和大家分享一下如何利用这个 API 实现 Element-Plus 的主题切换动画效果。
初识 View Transition API
View Transition API 是浏览器原生提供的一个强大工具,能够帮助开发者轻松实现页面状态切换时的过渡动画。它的核心思想是:当页面内容发生较大变化时(比如主题切换、页面跳转等),浏览器会自动为新旧内容分别创建快照,然后通过这些快照完成平滑的过渡效果。
这个 API 的使用相对简单,主要依赖于 document.startViewTransition() 方法。调用这个方法后,浏览器会捕获当前页面视图作为旧快照(::view-transition-old(root)),在回调函数中修改页面内容后,再捕获新视图作为新快照(::view-transition-new(root)),最后在 ::view-transition-image-pair(root) 容器中完成动画过渡。
基础主题切换实现
我们先从最简单的主题切换开始。创建一个 HTML 文件,添加一个按钮,并通过切换 CSS 变量来改变背景颜色,模拟主题的明暗切换。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>主题切换示例</title>
<style>
:root {
--bg-color: #fff;
background: var(--bg-color);
}
:root.dark {
--bg-color: #000;
}
</style>
</head>
<body>
<button id="themeButton">切换主题</button>
<script>
const themeButton = document.getElementById('themeButton');
themeButton.addEventListener('click', () => {
document.documentElement.classList.toggle('dark');
});
</script>
</body>
</html>
添加过渡动画
接下来,我们利用 View Transition API 为切换过程添加过渡动画。在按钮的点击事件中,调用 document.startViewTransition() 方法,将主题切换的逻辑放入其回调函数中。
themeButton.addEventListener('click', () => {
document.startViewTransition(() => {
document.documentElement.classList.toggle('dark');
});
});
这样,当我们点击按钮时,页面就会出现一个默认的过渡动画效果。
自定义圆形扩散动画
为了让动画效果更具特色,我们可以自定义一个圆形扩散动画。在调用 document.startViewTransition() 后,通过其返回的 Promise 对象,在动画开始时获取鼠标点击位置的坐标,然后计算出圆形扩散的最大半径。最后,利用 CSS 的 clipPath 属性结合 document.documentElement.animate() 方法,实现圆形从点击点扩散的动画效果。
themeButton.addEventListener('click', (e) => {
const transition = document.startViewTransition(() => {
document.documentElement.classList.toggle('dark');
});
transition.ready.then(() => {
const { clientX, clientY } = e;
const radius = Math.hypot(
Math.max(clientX, innerWidth - clientX),
Math.max(clientY, innerHeight - clientY)
);
document.documentElement.animate(
[
{ clipPath: `circle(0% at ${clientX}px ${clientY}px)` },
{ clipPath: `circle(${radius}px at ${clientX}px ${clientY}px)` }
],
{
duration: 300,
pseudoElement: "::view-transition-new(root)"
}
);
});
});
为了确保自定义动画正常工作,还需要在 CSS 中关闭 View Transition API 的默认动画效果:
::view-transition-new(root),
::view-transition-old(root) {
animation: none;
}
最终完整代码
将以上内容整合,得到完整的主题切换动画实现代码:
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>主题切换动画示例</title>
<style>
:root {
--bg-color: #fff;
background-color: var(--bg-color);
}
:root.dark {
--bg-color: #000;
}
::view-transition-new(root),
::view-transition-old(root) {
animation: none;
}
</style>
</head>
<body>
<button id="themeButton">切换主题</button>
<script>
const themeButton = document.getElementById("themeButton");
themeButton.addEventListener("click", (e) => {
const transition = document.startViewTransition(() => {
document.documentElement.classList.toggle("dark");
});
transition.ready.then(() => {
const { clientX, clientY } = e;
const radius = Math.hypot(
Math.max(clientX, innerWidth - clientX),
Math.max(clientY, innerHeight - clientY)
);
document.documentElement.animate(
{
clipPath: [
`circle(0% at ${clientX}px ${clientY}px)`,
`circle(${radius}px at ${clientX}px ${clientY}px)`,
],
},
{
duration: 500,
pseudoElement: "::view-transition-new(root)",
}
);
});
});
</script>
</body>
</html>
通过以上步骤,我们成功实现了一个具有圆形扩散过渡效果的网页主题切换功能。View Transition API 的强大之处在于它简化了复杂动画的实现过程,让开发者能够以较低的成本为用户提供换肤动画效果