使用 startViewTransition 实现主题切换效果
使用 startViewTransition 实现主题切换效果
Element Plus 的主题切换效果是一个非常精美的动画:点击主题按钮时,主题会从点击位置以圆形动画的形式扩散或收缩,为用户提供流畅的视觉体验。本文将介绍如何使用 Web API 中的 startViewTransition
实现这一效果。
实现代码
下面是实现 Element Plus 主题切换效果的完整代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Element Plus 主题切换效果</title>
<style>
* {
margin: 0;
padding: 0;
}
html {
background-color: white;
color: #303133;
transition: background-color 0.3s ease, color 0.3s ease;
}
html.dark {
background-color: #1a1a1a;
color: #f0f0f0;
}
button {
padding: 10px 20px;
margin: 20px;
background-color: #409eff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #66b1ff;
}
/* 配置视图过渡效果 */
::view-transition-new(root),
::view-transition-old(root) {
width: 100vw;
height: auto;
animation: none;
}
html.dark::view-transition-old(root) {
z-index: 9999;
}
html.dark::view-transition-new(root) {
z-index: 1;
}
html::view-transition-old(root) {
z-index: 1;
}
html::view-transition-new(root) {
z-index: 9999;
}
</style>
</head>
<body>
<button onclick="toggleTheme(event)">切换主题</button>
<script>
const buttonNode = document.querySelector("button");
let isDark = false;
function changeStyle() {
isDark = !isDark;
document.documentElement.classList.toggle("dark");
}
function toggleTheme(event) {
// 检查浏览器是否支持 startViewTransition
if (!document.startViewTransition) {
changeStyle();
return;
}
// 创建视图过渡
const transition = document.startViewTransition(() => changeStyle());
// 当过渡准备就绪时,设置动画效果
transition.ready.then(() => {
// 获取点击位置的坐标
const { clientX: x, clientY: y } = event;
// 计算覆盖整个页面所需的最大半径
const r = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y));
// 定义裁剪路径动画的两个状态
const clipPath = [
`circle(${r}px at ${x}px ${y}px)`,
`circle(0px at ${x}px ${y}px)`,
];
// 应用动画
document.documentElement.animate(
{
clipPath: isDark ? clipPath : clipPath.reverse(),
},
{
duration: 500,
easing: "ease-out",
// 根据主题切换方向选择动画目标
pseudoElement: isDark
? "::view-transition-old(root)"
: "::view-transition-new(root)",
}
);
});
}
</script>
</body>
</html>
代码解析
核心原理
该实现的核心原理是利用 View Transitions API
创建不同 DOM 状态之间的平滑过渡,并通过 clip-path
属性实现圆形裁剪动画效果。
HTML 结构
HTML 部分非常简洁,只包含一个用于触发主题切换的按钮。实际项目中,你可以将这个功能集成到现有的主题切换按钮上。
CSS 样式
基础样式:设置了默认和暗色主题的背景色和文字颜色,以及按钮的样式。
视图过渡配置:
::view-transition-new(root), ::view-transition-old(root) { width: 100vw; height: auto; animation: none; }
这部分禁用了默认的视图过渡动画,为我们自定义的动画效果做准备。
Z-index 控制:
html.dark::view-transition-old(root) { z-index: 9999; } /* 其他 z-index 配置 */
这些规则确保在主题切换过程中,新旧视图层叠关系正确,实现从点击位置扩散或收缩的视觉效果。
JavaScript 实现
状态管理:
let isDark = false; function changeStyle() { isDark = !isDark; document.documentElement.classList.toggle("dark"); }
维护当前主题状态,并通过切换
dark
类来改变主题。浏览器兼容性检查:
if (!document.startViewTransition) { changeStyle(); return; }
在不支持
startViewTransition
的浏览器中,直接切换主题,确保功能正常。视图过渡创建:
const transition = document.startViewTransition(() => changeStyle());
创建视图过渡,并在回调函数中执行主题切换。
动画配置:
transition.ready.then(() => { const { clientX: x, clientY: y } = event; const r = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y)); const clipPath = [ `circle(${r}px at ${x}px ${y}px)`, `circle(0px at ${x}px ${y}px)`, ]; });
- 获取点击位置的坐标
- 使用
Math.hypot()
计算覆盖整个页面所需的最大半径 - 定义裁剪路径的起始和结束状态
应用动画:
document.documentElement.animate( { clipPath: isDark ? clipPath : clipPath.reverse(), }, { duration: 500, easing: "ease-out", pseudoElement: isDark ? "::view-transition-old(root)" : "::view-transition-new(root)", } );
- 根据主题切换方向(亮到暗或暗到亮)确定动画方向
- 设置动画持续时间、缓动函数
- 指定动画应用的伪元素(旧视图或新视图)
使用方法
基本使用
将上面的完整代码保存为 HTML 文件,在浏览器中打开即可看到效果。
点击"切换主题"按钮,即可看到圆形动画效果:
- 从亮色切换到暗色时:从点击位置向外收缩
- 从暗色切换到亮色时:从点击位置向内扩散
集成到现有项目
要将此效果集成到现有项目中,只需执行以下步骤:
在你的 CSS 文件中添加视图过渡相关的样式:
::view-transition-new(root), ::view-transition-old(root) { width: 100vw; height: auto; animation: none; } html.dark::view-transition-old(root) { z-index: 9999; } html.dark::view-transition-new(root) { z-index: 1; } html::view-transition-old(root) { z-index: 1; } html::view-transition-new(root) { z-index: 9999; }
修改你的主题切换函数,加入视图过渡逻辑:
let isDark = false; // 或者从 localStorage 或其他地方获取当前主题状态 function toggleTheme(event) { if (!document.startViewTransition) { document.documentElement.classList.toggle('dark'); isDark = !isDark; return; } const transition = document.startViewTransition(() => { document.documentElement.classList.toggle('dark'); isDark = !isDark; }); transition.ready.then(() => { const { clientX: x, clientY: y } = event; const r = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y)); const clipPath = [ `circle(${r}px at ${x}px ${y}px)`, `circle(0px at ${x}px ${y}px)`, ]; document.documentElement.animate( { clipPath: isDark ? clipPath : clipPath.reverse(), }, { duration: 500, easing: "ease-out", pseudoElement: isDark ? "::view-transition-old(root)" : "::view-transition-new(root)", } ); }); }
确保你的主题切换按钮调用
toggleTheme
函数并传递事件对象:<button onclick="toggleTheme(event)">切换主题</button>
应用场景
1. 网站主题切换
任何需要实现亮色/暗色主题切换的网站都可以使用此效果,为用户提供平滑的视觉过渡。
2. 品牌色切换
除了明暗主题,还可以扩展此效果用于品牌色或主题风格的切换,例如不同节日主题的切换。
3. 单页应用状态转换
在单页应用中,可以使用类似的技术在不同状态之间创建平滑过渡,例如登录前后的界面变化。
4. 数据可视化主题切换
在数据可视化应用中,切换图表主题时使用此效果,可以使数据展示更加流畅,减少视觉跳跃感。
浏览器兼容性
相关信息
- 表格中的数字表示该浏览器开始支持的最低版本
- "No" 表示该浏览器不支持此功能
PC 端
浏览器功能 | Chrome | Edge | Firefox | Opera | Safari |
---|---|---|---|---|---|
View Transitions API | 107 | 107 | No | 93 | No |
移动端
浏览器功能 | Chrome Android | Firefox Android | Opera Android | Safari iOS | WebView Android |
---|---|---|---|---|---|
View Transitions API | 107 | No | 73 | No | 107 |
注意事项
- IE 浏览器兼容性:Internet Explorer 浏览器完全不支持 View Transitions API
- 降级处理:代码中已经包含了降级处理逻辑 (
if (!document.startViewTransition)
),在不支持的浏览器中,主题切换功能仍然可以正常工作,只是没有动画效果 - Firefox 支持情况:Firefox 目前不支持 View Transitions API,但计划在未来版本中支持
- Safari 支持情况:Safari 浏览器目前不支持 View Transitions API
更新日志
38e56
-于