携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情
后台项目前端综合解决方案之通用功能开发
动态换肤原理分析
所谓的动态换肤,说白一点就是涉及到的色值不可以写死
那么我们就先来说一下动态换肤的实现方法
在 scss
中,我们可以通过 $变量名:变量值
的方式定义 css
变量,然后通过该 css
变量去指定某一块 DOM
对应的颜色
此时,如果我们改变了 css
变量的值,那么对应的 DOM
的颜色也会改变,试想一下,如果有大量的 DOM
依赖于该 css
变量,那么我们改变这个变量时,是不是就会使得所有涉及的 DOM
的颜色就都会改变,这个其实就是所谓的动态换肤
而在项目中,我们要实现动态换肤需要处理的有两个方面
1、element-plus
的主题
2、非 element-plus
的主题
动态换肤实现方案分析
从上边的原理我们可以得到两个关键的信息
1、动态换肤的关键在于修改 css
的值
2、换肤需要同时兼顾 element-plus
以及 非 element-plus
根据以上信息,我们就可以得出对应的实现方案
1、创建一个组件 ThemeSelect
用来处理修改之后的 css
变量的值
2、根据新值修改 element-plus
主题色
3、根据心智修改非 element-plus
主题色
创建 ThemeSelect 组件
创建 components/ThemeSelect/index
组件
<template>
<!-- 主题图标
v-bind:https://v3.cn.vuejs.org/api/instance-properties.html#attrs -->
<el-dropdown
v-bind="$attrs"
trigger="click"
class="theme"
@command="handleSetTheme"
>
<div>
<el-tooltip :content="$t('msg.navBar.themeChange')">
<svg-icon icon="change-theme" />
</el-tooltip>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="color">
{{ $t('msg.theme.themeColorChange') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- 展示弹出层 -->
<div></div>
</template>
<script setup>
const handleSetTheme = command => {}
</script>
<style lang="scss" scoped></style>
在 layout/components/Navbar/index.vue
中进行引入
<div class="right-menu">
<theme-picker class="right-menu-item hover-effect"></theme-picker>
</div>
import ThemePicker from '@/components/ThemeSelect/index'
创建 SelectColor 组件
在有了 ThemeSelect
之后,接下来我们处理颜色选择器组件 SelectColor
,在这里我们会用到 element
的 el-color-picker
组件
对于 SelectColor
的处理,我们需要分成两步
1、完成 SelectColor
弹窗的双向数据绑定
2、把选中的色值进行本地缓存
首先来看第一步
创建 components/ThemePicker/components/SelectColor.vue
<template>
<el-dialog title="提示" :model-value="modelValue" @close="closed" width="22%">
<div class="center">
<p class="title">{{ $t('msg.theme.themeColorChange') }}</p>
<el-color-picker
v-model="mColor"
:predefine="predefineColors"
></el-color-picker>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="closed">{{ $t('msg.universal.cancel') }}</el-button>
<el-button type="primary" @click="comfirm">{{
$t('msg.universal.confirm')
}}</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { defineProps, defineEmits, ref } from 'vue'
defineProps({
modelValue: {
type: Boolean,
required: true
}
})
const emits = defineEmits(['update:modelValue'])
// 预定义色值
const predefineColors = [
'#ff4500',
'#ff8c00',
'#ffd700',
'#90ee90',
'#00ced1',
'#1e90ff',
'#c71585',
'rgba(255, 69, 0, 0.68)',
'rgb(255, 120, 0)',
'hsv(51, 100, 98)',
'hsva(120, 40, 94, 0.5)',
'hsl(181, 100%, 37%)',
'hsla(209, 100%, 56%, 0.73)',
'#c7158577'
]
// 默认色值
const mColor = ref('#00ff00')
/**
* 关闭
*/
const closed = () => {
emits('update:modelValue', false)
}
/**
* 确定
* 1. 修改主题色
* 2. 保存最新的主题色
* 3. 关闭 dialog
*/
const comfirm = async () => {
// 3. 关闭 dialog
closed()
}
</script>
<style lang="scss" scoped>
.center {
text-align: center;
.title {
margin-bottom: 12px;
}
}
</style>
在 ThemePicker/index
中使用该组件
<template>
<!-- 展示弹出层 -->
<div>
<select-color v-model="selectColorVisible"></select-color>
</div>
</template>
<script setup>
import SelectColor from './components/SelectColor.vue'
import { ref } from 'vue'
const selectColorVisible = ref(false)
const handleSetTheme = command => {
selectColorVisible.value = true
}
</script>
至此双向数据绑定已经完成,下边需要进行的是色值的本地缓存
缓存方式分为两种
1、vuex
2、本地存储
在 constants/index
下新建常量值
// 主题色保存的 key
export const MAIN_COLOR = 'mainColor'
// 默认色值
export const DEFAULT_COLOR = '#409eff'
创建 store/modules/theme
模块,用来处理主题色相关内容
import { getItem, setItem } from '@/utils/storage'
import { MAIN_COLOR, DEFAULT_COLOR } from '@/constant'
export default {
namespaced: true,
state: () => ({
mainColor: getItem(MAIN_COLOR) || DEFAULT_COLOR
}),
mutations: {
/**
* 设置主题色
*/
setMainColor(state, newColor) {
state.mainColor = newColor
setItem(MAIN_COLOR, newColor)
}
}
}
在 store/getters
下指定快捷访问
mainColor: state => state.theme.mainColor
在 store/index
中导入 theme
import theme from './modules/theme.js'
export default createStore({
getters,
modules: {
theme
}
})
在 selectColor
中,设置初始色值和缓存色值
<script setup>
import { defineProps, defineEmits, ref } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
// 默认色值
const mColor = ref(store.getters.mainColor)
/**
* 确定
* 1. 修改主题色
* 2. 保存最新的主题色
* 3. 关闭 dialog
*/
const comfirm = async () => {
// 2. 保存最新的主题色
store.commit('theme/setMainColor', mColor.value)
// 3. 关闭 dialog
closed()
}
</script>