前端中有用到需要设置主题颜色,根据用户喜欢实现换肤。今天分享一下使用css中 :root 变量来实现换肤、主题色的功能
先看图,再看代码
先准备工具方法,用于转换颜色值的格式
1、将一个十六进制颜色值转换成RGB颜色值
/**
* 将一个十六进制颜色值转换成RGB颜色值
* @param str 一个十六进制颜色值
* @returns
*/
export function hexToRgb(str: string) {
let hexs: any = ''
const reg = /^#?[0-9A-Fa-f]{6}$/
if (!reg.test(str)) {
return ElMessage.warning('输入错误的hex')
}
str = str.replace('#', '')
hexs = str.match(/../g)
for (let i = 0; i < 3; i++) {
hexs[i] = parseInt(hexs[i], 16)
}
return hexs
}
2、将RGB颜色值转换成一个十六进制颜色值
/**
* 将RGB颜色值转换成一个十六进制颜色值
* @param r 表示红颜色通道的十进制数值
* @param g 表示绿颜色通道的十进制数值
* @param b 表示蓝颜色通道的十进制数值
* @returns
*/
export function rgbToHex(r: any, g: any, b: any) {
const reg = /^\d{1,3}$/
if (!reg.test(r) || !reg.test(g) || !reg.test(b)) {
return ElMessage.warning('输入错误的rgb颜色值')
}
const hexs = [r.toString(16), g.toString(16), b.toString(16)]
for (let i = 0; i < 3; i++) {
if (hexs[i].length == 1) {
hexs[i] = `0${hexs[i]}`
}
}
return `#${hexs.join('')}`
}
3、根据给定的十六进制颜色值和暗度级别来生成一个更暗的颜色
/**
* 根据给定的十六进制颜色值和暗度级别来生成一个更暗的颜色
* @param color 表示颜色值
* @param level 表示亮度级别
* @returns
*/
export function getDarkColor(color: string, level: number) {
const reg = /^\#?[0-9A-Fa-f]{6}$/
if (!reg.test(color)) {
return ElMessage.warning('输入错误的hex颜色值')
}
const rgb = hexToRgb(color)
for (let i = 0; i < 3; i++) {
rgb[i] = Math.round(20.5 * level + rgb[i] * (1 - level))
}
return rgbToHex(rgb[0], rgb[1], rgb[2])
}
4、根据给定的十六进制颜色值和亮度级别来生成一个更亮的颜色
/**
* 根据给定的十六进制颜色值和亮度级别来生成一个更亮的颜色
* @param color 表示颜色值
* @param level 表示亮度级别
* @returns
*/
export function getLightColor(color: string, level: number) {
const reg = /^#?[0-9A-Fa-f]{6}$/
if (!reg.test(color)) {
return ElMessage.warning('输入错误的hex颜色值')
}
const rgb = hexToRgb(color)
for (let i = 0; i < 3; i++) {
rgb[i] = Math.round(255 * level + rgb[i] * (1 - level))
}
return rgbToHex(rgb[0], rgb[1], rgb[2])
}
5、定义颜色值的全局状态
import { defineStore } from 'pinia'
import { themeColor, otherColor } from '@/config/themeColor'
import type { ISettingsState } from '../types/setting'
export const useSettingsStore = defineStore('settings', {
state: (): ISettingsState => ({
collapse: false,
refresh: false, // 刷新页面
isDark: false,
otherColor: {
menubgcolor: otherColor.DEFAULT_MENUGACKGROUND,
},
themeColor: {
primary: themeColor.DEFAULT_PRIMARY,
success: themeColor.DEFAULT_SUCCESS,
warning: themeColor.DEFAULT_WARNING,
danger: themeColor.DEFAULT_DANGER,
info: themeColor.DEFAULT_INFO,
},
}),
actions: {
changeCollapse() {
this.collapse = !this.collapse
},
setRefresh() {
this.refresh = !this.refresh
},
setIsDark() {
this.isDark = !this.isDark
},
setOtherColor(colorType: string, val: string){
this.otherColor[colorType] = val
},
setThemeConfig(colorType: string, val: string) {
this.themeColor[colorType] = val
},
},
persist: true,
})
6、具体修改颜色值后调用方法
import { computed } from 'vue'
import i18n from '@/i18n/index'
import { ElMessage } from 'element-plus'
import { getDarkColor, getLightColor } from '@/utils/colorConversion'
import { useSettingsStore } from '@/store/modules/setting'
export const useTheme = () => {
const settingsStore = useSettingsStore()
const themeColor = computed(() => settingsStore.themeColor)
const isDark = computed(() => settingsStore.isDark)
// 切换暗黑模式
const switchDark = () => {
const body = document.documentElement as HTMLElement
if (isDark.value) {
body.setAttribute('class', 'dark')
} else {
body.setAttribute('class', '')
}
changeThemeColor(themeColor.value.primary, 'primary', true)
}
// 修改主题颜色
const changeThemeColor = (
val: string,
colorType = 'primary',
isInit = false
) => {
if (!isInit) {
ElMessage.success(`${i18n.global.t('settings.colorSetTip')} ${val}`)
}
settingsStore.setThemeConfig(colorType, val)
document.documentElement.style.setProperty(
`--el-color-${colorType}`,
themeColor.value[colorType]
)
// 颜色加深或变浅
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(
`--el-color-${colorType}-light-${i}`,
isDark.value
? `${getDarkColor(themeColor.value[colorType], i / 10)}`
: `${getLightColor(themeColor.value[colorType], i / 10)}`
)
}
}
// 修改其他颜色值
const changeOtherColor = (colorType: string, val: string) => {
settingsStore.setOtherColor(colorType, val)
document.documentElement.style.setProperty(`--${colorType}`, val)
}
// 初始化主题
const initTheme = () => {
const colorArr = Object.keys(themeColor.value)
for (let index = 0; index < colorArr.length; index++) {
const color = colorArr[index]
changeThemeColor(themeColor.value[color], color, true)
}
switchDark()
}
return {
initTheme,
switchDark,
changeThemeColor,
changeOtherColor
}
}
7、组件页面的代码
<template>
<el-drawer title="主题设置" v-model="drawerVisible" size="300px">
<el-divider class="divider" content-position="center">全局主题</el-divider>
<div class="theme-item">
<span>主题颜色</span>
<el-color-picker
v-model="ThemeColorObj.primary"
:predefine="colorList"
@change="changeThemeColor(ThemeColorObj.primary, 'primary')"
/>
</div>
<div class="theme-item">
<span>成功颜色</span>
<el-color-picker
v-model="ThemeColorObj.success"
@change="changeThemeColor(ThemeColorObj.success, 'success')"
/>
</div>
<div class="theme-item">
<span>警告颜色</span>
<el-color-picker
v-model="ThemeColorObj.warning"
@change="changeThemeColor(ThemeColorObj.warning, 'warning')"
/>
</div>
<div class="theme-item">
<span>危险颜色</span>
<el-color-picker
v-model="ThemeColorObj.danger"
@change="changeThemeColor(ThemeColorObj.danger, 'danger')"
/>
</div>
<div class="theme-item">
<span>信息颜色</span>
<el-color-picker
v-model="ThemeColorObj.info"
@change="changeThemeColor(ThemeColorObj.info, 'info')"
/>
</div>
<div class="theme-item">
<span>暗黑模式</span>
<el-switch
v-model="isDarkValue"
@change="onAddDarkChange"
inline-prompt
:active-icon="Sunny"
:inactive-icon="Moon"
/>
</div>
<div class="theme-item">
<span>菜单背景色</span>
<el-color-picker
v-model="otherColorObj.menubgcolor"
@change="changeOtherColor('menubgcolor', otherColorObj.menubgcolor)"
/>
</div>
</el-drawer>
</template>
<script setup lang="ts">
import mittBus from '@/utils/mittBus'
import { themeColor } from '@/config/themeColor'
import { useSettingsStore } from '@/store/modules/setting'
import { useTheme } from '@/hooks/useTheme'
import { Sunny, Moon } from '@element-plus/icons-vue'
const { changeThemeColor, switchDark, changeOtherColor } = useTheme()
const settingsStore = useSettingsStore()
const ThemeColorObj = computed(() => settingsStore.themeColor)
const otherColorObj = computed(() => settingsStore.otherColor)
const isDarkValue = computed(() => settingsStore.isDark)
const onAddDarkChange = () => {
settingsStore.setIsDark()
switchDark()
}
// 预定义主题颜色
const colorList = [
themeColor.DEFAULT_PRIMARY,
'#DAA96E',
'#0C819F',
'#722ed1',
'#27ae60',
'#ff5c93',
'#e74c3c',
'#fd726d',
'#f39c12',
'#9b59b6',
]
// 打开主题设置
const drawerVisible = ref(false)
mittBus.on('openThemeDrawer', () => {
drawerVisible.value = true
})
</script>
<style scoped lang="scss">
.theme-item {
display: flex;
align-items: center;
justify-content: space-between;
margin: 14px 0;
}
</style>
到此就完成了动态修改:root变量实现换肤了,具体的应用场景中我们应该是在页面上操作,选中需要的肤色然后修改:root定义的变量值,考虑到浏览器刷新会重置原始状态的值,这里要结合本地存储方式记住选中的肤色。如有问题欢迎大家指出!