vue深色模式浅色模式切换思路
其实现在的组件库支持浅色深色模式切换的很多,听说tailwindCss好像很方便,但是我还没用过,我用elemen-plus比较多,先记录一篇vueuse + elementplus 主题切换思路
首先 安装vueuse 和elementplus,这里就不做演示了
根目录创建composables,这里封装下vueuse我们需要用到的方法和参数
在composables目录下创建dark.ts和index.ts
// dark.ts
import { useDark, useToggle } from '@vueuse/core'
export const isDark = useDark()
export const toggleDark = useToggle(isDark)
// index.ts
export * from './dark'
然后写一下公共样式
//index.scss
// import dark theme
@use 'element-plus/theme-chalk/src/dark/css-vars.scss' as *;
// :root {
// --ep-color-primary: red;
// }
body {
font-family: Inter, system-ui, Avenir, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
height: 100vh;
margin: 0;
}
#app {
width: 100vw;
height: 100vh;
}
a {
color: var(--ep-color-primary);
}
code {
border-radius: 2px;
padding: 2px 4px;
background-color: var(--ep-color-primary-light-9);
color: var(--ep-color-primary);
}
然后在main.ts中引入index.scss
import 'element-plus/dist/index.css'
import '@/styles/index.scss' //注意一定得在element-plus/dist/index.css后引入
然后封装一个切换主题的组件
// ChangeTheme.vue
<script setup lang="ts">
import {nextTick, ref, watch} from 'vue'
import type {SwitchInstance} from 'element-plus'
import {useDark, useToggle} from "@vueuse/core";
defineOptions({inheritAttrs: false})
const isDark = useDark()
const darkMode = ref(isDark)
const switchRef = ref<SwitchInstance>()
watch(
() => darkMode.value,
() => {
useToggle()
}
)
const beforeChange = () => {
return new Promise<boolean>((resolve) => {
const isAppearanceTransition =
document.startViewTransition &&
!window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (!isAppearanceTransition) {
resolve(true)
return
}
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 ratioX = (100 * x) / innerWidth
const ratioY = (100 * y) / innerHeight
const referR = Math.hypot(innerWidth, innerHeight) / Math.SQRT2
const ratioR = (100 * endRadius) / referR
const transition = document.startViewTransition(async () => {
resolve(true)
await nextTick()
})
transition.ready.then(() => {
const clipPath = [
`circle(0% at ${ratioX}% ${ratioY}%)`,
`circle(${ratioR}% at ${ratioX}% ${ratioY}%)`,
]
document.documentElement.animate(
{
clipPath: isDark.value ? [...clipPath].reverse() : clipPath,
},
{
duration: 400,
easing: 'ease-in',
pseudoElement: isDark.value
? '::view-transition-old(root)'
: '::view-transition-new(root)',
}
)
})
})
}
</script>
<template>
<el-switch
ref="switchRef"
v-model="darkMode"
v-bind="$attrs"
inline-prompt
style="--el-switch-on-color: rgba(176,176,176,0.3); --el-switch-off-color: #409EFF"
:before-change="beforeChange"
active-icon="Moon"
inactive-icon="Sunny"
/>
</template>
<style lang="scss" scoped>
:deep(.dark-icon) {
border-radius: 50%;
color: #cfd3dc;
background-color: #141414;
}
:deep(.light-icon) {
color: #606266;
}
</style>
大功告成

浙公网安备 33010602011771号