😀 在 Web 开发中,主题切换通常是即时生效的,但这种方式会导致用户视觉上的突兀感。通过 View Transitions API,我们可以实现更流畅的切换动画,让深色模式和浅色模式的过渡更加自然。
功能描述
点击 Switch 组件切换主题时,页面会从点击位置向外扩散,形成类似"水波扩散"的动画效果,使切换过程更具视觉吸引力。
核心代码
- 主题切换逻辑 在主题切换处理函数中:
ts
const handleThemeChange: SwitchProps["onChange"] = (checked: boolean, event) => {
// 必须加这句
document.documentElement.className = checked ? "dark" : "light";
// 获取点击位置,或者回退到页面中间
const x = event instanceof MouseEvent ? event?.clientX / 2 : innerWidth;
console.log(x);
const y = event instanceof MouseEvent ? event?.clientY / 2 : innerHeight;
// 获取到最远角的距离
const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y));
const circlePath = [`circle(0 at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`];
// 执行主题切换
const transition = document.startViewTransition(() => {
setIsDarkMode(checked);
});
// 监听过渡动画的就绪状态
transition.ready.then(() => {
document.documentElement.animate(
{
clipPath: checked ? circlePath : [...circlePath].reverse(),
},
{
duration: 1000,
easing: "ease-in",
pseudoElement: checked ? "::view-transition-new(root)" : "::view-transition-old(root)",
}
);
});
};
document.startViewTransition
:执行 dom 更改代码,在 React 中则为发起渲染更新。
transition.ready.then
:前后两张快照之间的过渡动画。
- 配置全局样式
为了确保 View Transitions 正常工作,需要添加一些 CSS 规则:
css
/** Animated Theme Toggle */
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
}
.dark::view-transition-old(root) {
z-index: 1;
}
.dark::view-transition-new(root) {
z-index: 999;
}
::view-transition-old(root) {
z-index: 999;
}
::view-transition-new(root) {
z-index: 1;
}
总结
- 核心思路:利用 View Transitions API,通过 clipPath 创建动态扩散动画。
- 动画细节:根据鼠标点击位置,计算扩散路径,使视觉效果更自然。
- 兼容性:目前 View Transitions 主要支持 Chrome 111+,其他浏览器支持情况可参考 Can I Use。
这种方式不仅提升了用户体验,还让主题切换更加流畅和现代化。🚀