实现效果

实现过程
前提需要引入好 element-plus,并导入element的黑色主题CSS
示例,再 main.js 中引入
1 2 3 4 5 6 7 8
| import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import 'element-plus/theme-chalk/dark/css-vars.css'
app.use(ElementPlus, { locale: locale, size: Cookies.get('size') || 'default' })
|
接着编写下面的基础代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <template> <div> <el-switch v-model='value1' class='switch' @change='changeSwitch'> <template #active-action> <el-icon> <Moon /> </el-icon> </template> <template #inactive-action> <el-icon> <Sunny /> </el-icon> </template> </el-switch> </div> </template>
<script setup> let value1 = ref(false)
function changeSwitch() { document.documentElement.classList.toggle('dark') } </script> <style scoped lang='scss'> .switch { position: absolute; left: 200px; top: 200px; } </style>
|
现在当切换 switch 时,背景颜色会直接进行黑白切换

从动画可以看到,现在是没有任何过渡效果的
我们可以使用 document.startViewTransition
很简单的就可以加上过渡效果,document.startViewTransition
接收一个函数参数,再函数内部写上操作DOM的逻辑代码即可实现一个默认的过度效果
1 2 3 4 5
| function changeSwitch() { document.startViewTransition(() => { document.documentElement.classList.toggle('dark') }) }
|
现在就有了一个默认的过渡效果

为什么使用了 document.startViewTransition
就会有一个动画效果呢
MDN文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Document/startViewTransition
通过调用 API,让浏览器为新旧两种不同视图分别捕获并建立了快照 (即 ::view-transition-old(root)旧快照 和 ::view-transition-new(root) 新快照),而后新旧两快照在 ::view-transition-image-pair(root) 容器中完成转场动画的过渡。动画结束后则删除其相关伪元素 (快照和容器)。
我们也可以控制默认的动画时长
1 2 3 4
| ::view-transition-old(root), /* 旧视图*/ ::view-transition-new(root) { /* 新视图*/ animation-duration: 2s; }
|
了解了这些,现在想点击按钮,实现一个扩散圆来实现主题切换效果,只需要根据按钮位置,计算圆的半径,然后设置 ::view-transition-new(root)
这个伪元素的动画效果,让圆从 0% 到 100%即可
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| <template> <div> <el-switch v-model='isDark' class='switch' ref='switchRef' @change='changeSwitch'> <template #active-action> <el-icon> <Moon /> </el-icon> </template> <template #inactive-action> <el-icon> <Sunny /> </el-icon> </template> </el-switch> </div> </template>
<script setup> import { ref } from 'vue'
let isDark = ref(false) let switchRef = ref(null)
function changeSwitch() { const transition = document.startViewTransition(() => { document.documentElement.classList.toggle('dark') })
transition.ready.then(() => { const switchElement = switchRef.value?.$el const rect = switchElement.getBoundingClientRect() const x = rect.left + rect.width / 2 const y = rect.top + rect.height / 2 const endRadius = Math.hypot( Math.max(x, innerWidth - x), Math.max(y, innerHeight - y), ) const clipPath = [ `circle(0 at ${x}px ${y}px)`, `circle(${endRadius}px at ${x}px ${y}px)`, ] document.documentElement.animate( { clipPath: isDark.value ? clipPath.reverse() : clipPath, }, { duration: 400, pseudoElement: isDark.value ? '::view-transition-old(root)' : '::view-transition-new(root)', }, ) }) } </script>
<style> ::view-transition-new(root), ::view-transition-old(root) { animation: none !important; }
.dark::view-transition-old(root) { z-index: 100; } </style>
<style scoped> .switch { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); } </style>
|
circle 表示画一个圆形,更多图像可参考
https://developer.mozilla.org/zh-CN/docs/Web/CSS/clip-path#%E5%BD%A2%E5%BC%8F%E8%AF%AD%E6%B3%95
关于更多的 document.documentElement.animate 知识可参考
https://developer.mozilla.org/zh-CN/docs/Web/API/Element/animate