一、方式一:自定义主题(常规版)
- 在 src/assets/theme.scss 中定义不同主题变量
html[data-theme='default'] {
--color-page-bg: #fff;
--color-page-fs: #000;
--size-page-fs: 20px;
}
html[data-theme='dark'] {
--color-page-bg: #000;
--color-page-fs: #fff;
--size-page-fs: 20px;
}
html[data-theme='red'] {
--color-page-bg: red;
--color-page-fs: #fff;
--size-page-fs: 20px;
}
- 在入口 main.js 中注册
import '@/assets/theme.scss'
- 在 index.html 中,定义 data-theme 属性和默认主题值 default
<!doctype html>
<html lang="en" data-theme="default">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
- 在 src/App.vue 中,初始化主题
<script setup>
import { RouterView } from 'vue-router'
import { ref } from 'vue'
// 初始化主题
if (localStorage.getItem('current-theme')) {
const currentTheme = ref(localStorage.getItem('current-theme'))
document.documentElement.setAttribute('data-theme', currentTheme.value)
}
</script>
<template>
<RouterView />
</template>
- 在 src/views/HomeView.vue 中,实现切换主题
<script setup>
// 切换主题
const fnToggle = (theme) => {
localStorage.setItem('current-theme', theme)
document.documentElement.setAttribute('data-theme', theme)
}
const themeOptions = [
{
label: '默认主题',
value: 'default'
},
{
label: '暗黑主题',
value: 'dark'
},
{
label: '红色主题',
value: 'red'
}
]
</script>
<template>
<div class="wrapper">
<h1>vue 主题切换</h1>
<div>hello world</div>
<button v-for="item in themeOptions" :key="item.label" @click="fnToggle(item.value)">
{{ item.label }}
</button>
</div>
</template>
<style>
.wrapper {
height: 100vh;
width: 100vw;
overflow-y: auto;
box-sizing: border-box;
background: var(--color-page-bg);
color: var(--color-page-fs);
font-size: var(--size-page-fs);
}
</style>
二、方式二:通过 style.filter 属性实现主题切换
- 在 src/App.vue 中实现
<script setup>
import { ref } from 'vue'
// 1、初始化模式、主题
const colorAreaValue = ref(Number(localStorage.getItem('style-filter-color')) || 0)
const isDark = localStorage.getItem('style-filter-dark') === 'true' ? true : false
const darkValue = ref(isDark || false)
if (!darkValue.value) {
document.documentElement.style.filter = `hue-rotate(${colorAreaValue.value}deg)`
} else {
document.documentElement.style.filter = `invert(${darkValue.value ? '85' : '0'}%)`
}
// 2、切换主题
const changeTheme = (v) => {
document.documentElement.style.filter = `hue-rotate(${v}deg)`
localStorage.setItem('style-filter-color', v)
localStorage.setItem('style-filter-dark', false)
darkValue.value = false // 主题变更要切换到普通模式
}
// 3、切换模式
const changeMode = (v) => {
document.documentElement.style.filter = `invert(${v ? '85' : '0'}%)`
localStorage.setItem('style-filter-dark', v)
if (!v) {
document.documentElement.style.filter = `hue-rotate(${colorAreaValue.value}deg)` // 切换到普通模式、主题要生效
}
}
// 置灰模式
const changeGrayMode = () => {
document.documentElement.style.filter = `grayscale(100%)`
}
</script>
<template>
<div class="slider-demo-block">
<div style="width: 40px">主题</div>
<el-slider v-model="colorAreaValue" @input="changeTheme" show-input :max="360" />
</div>
<div class="slider-demo-block">
<div style="width: 40px">模式</div>
<el-switch
v-model="darkValue"
@change="changeMode"
class="ml-2"
inline-prompt
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
active-text="普通模式"
inactive-text="暗黑模式"
/>
</div>
<div style="margin: 20px 0">
<el-button type="primary" @click="changeGrayMode">置灰模式</el-button>
</div>
<div class="mb-4">
<el-button>Default</el-button>
<el-button type="primary">Primary</el-button>
<el-button type="success">Success</el-button>
<el-button type="info">Info</el-button>
<el-button type="warning">Warning</el-button>
<el-button type="danger">Danger</el-button>
</div>
</template>
<style lang="scss">
body {
background: #f9f9f9;
}
.slider-demo-block {
max-width: 600px;
display: flex;
align-items: center;
}
.slider-demo-block .el-slider {
margin-top: 0;
margin-left: 12px;
}
</style>
- 由于此方式是借助 css 的 filter 过滤属性实现的主题、模式切换,因此:
1. 此方式不需要定义主题文件
2. 此方式是通过 document.documentElement.style.filter 将过滤属性作用于全局
3. 此方式无法操作局部颜色
网友评论