Vue3+ElementPlus+TS 动态更换主题
2022-11-20 本文已影响0人
硅谷干货
一、思路分析
-
网上有的修改主题的思路都是自定义样式文件,全局引入,动态修改根dom的Class命名空间,然而这种方式非常麻烦,并且不能动态更改,目前根据官方网站推荐的做法,最佳实践就是直接覆盖全局变量。
/* element官网指南 */ // document.documentElement is global const el = document.documentElement // const el = document.getElementById('xxx') // get css var getComputedStyle(el).getPropertyValue(`--el-color-primary`) // set css var el.style['--el-color-primary'] = 'red' // set css var 如果 style 中没有这个变量需要这样设置 el.style.setProperty('--el-color-primary', red) 复制代码
-
打开
elementPlus官网
-> F12 ->查看样式。
直接划重点,那就是覆盖下面这些变量,其他颜色暂时不要去管他们(因为是一些字体、边框、以及渐变)
:root{ --el-color-primary: #409eff; --el-color-primary-light-1: #53a8ff; --el-color-primary-light-2: #66b1ff; --el-color-primary-light-3: #79bbff; --el-color-primary-light-4: #8cc5ff; --el-color-primary-light-5: #a0cfff; --el-color-primary-light-6: #b3d8ff; --el-color-primary-light-7: #c6e2ff; --el-color-primary-light-8: #d9ecff; --el-color-primary-light-9: #ecf5ff; --el-color-primary-dark-2: #337ecc } 复制代码
但是问题来了,如何根据主色调计算出下面的次级色调?
二、Sass mix函数JS翻译(最重要的一部分)
查看element-Plus样式源码 elementPlus/theme-chalk/src/common/var.scss.
你会发现组件的颜色是基本都基于主色调和次色调的全局变量,这就是为什么要覆盖变量的原因。
核心代码如下,这段 scss 就是生成 --el-color-primari-light-num 这些次级色调的。可以看到这里使用了一盒sass颜色函数mix,这个函数是如何实现的暂时不管,先用js翻译下面代码。
@mixin set-color-type-light($type, $number) {
$colors: map.deep-merge(
(
$type: (
'light-#{$number}':
mix(
$color-white,
map.get($colors, $type, 'base'),
math.percentage(math.div($number, 10))
),
),
),
$colors
) !global;
}
// $colors.primary.light-i
// --el-color-primary-light-i
// 10% 53a8ff
// 20% 66b1ff
// 30% 79bbff
// 40% 8cc5ff
// 50% a0cfff
// 60% b3d8ff
// 70% c6e2ff
// 80% d9ecff
// 90% ecf5ff
@for $i from 1 through 9 {
@each $type in $types {
@include set-color-type-light($type, $i);
}
}
复制代码
翻译后:
const node = document.documentElement;
// 前缀
const pre = "--el-color-primary";
// 主色调(可以使用el-color-picker绑定)
const color = "#409eff";
// 源码中的$color-white,也就是白色
const mixWhite = "#ffffff"
// 直接为根设置内联样式覆盖:root选择器的样式
node.style.setProperty(pre, color);
for (let i = 1; i < 10; i += 1) {
// 同理
node.style.setProperty(`${pre}-light-${i}`, mix(color, mixWhite, i * 0.1));
}
复制代码
mix函数原理:mix(color1 = rgb(r1,g1,b1),color2 = rgb(r2,g2,b2),weight)
参数一二为颜色,参数3位权重百分比,权重就是颜色中每个数值所占的比重
r = (r1 * (1 - weight) + r2 * weight);
g = (g1 * (1 - weight) + g2 * weight);
b = (b1 * (1 - weight) + b2 * weight);
finalColor = rgb(r,g,b);
复制代码
但是问题在于,js 中显然没法直接使用 rgb 颜色,所以要先将16进制颜色先转化成 rgb ,再计算,最后还要将得出的 rgb 颜色转化成16进制颜色(不转直接使用字符串的 rgb 也可以)。
const mix = (color1: string, color2: string, weight: number) => {
weight = Math.max(Math.min(Number(weight), 1), 0)
const r1 = parseInt(color1.substring(1, 3), 16)
const g1 = parseInt(color1.substring(3, 5), 16)
const b1 = parseInt(color1.substring(5, 7), 16)
const r2 = parseInt(color2.substring(1, 3), 16)
const g2 = parseInt(color2.substring(3, 5), 16)
const b2 = parseInt(color2.substring(5, 7), 16)
const r = Math.round(r1 * (1 - weight) + r2 * weight)
const g = Math.round(g1 * (1 - weight) + g2 * weight)
const b = Math.round(b1 * (1 - weight) + b2 * weight)
const _r = ('0' + (r || 0).toString(16)).slice(-2)
const _g = ('0' + (g || 0).toString(16)).slice(-2)
const _b = ('0' + (b || 0).toString(16)).slice(-2)
return '#' + _r + _g + _b
}
复制代码
三、在你的项目中使用
1.安装依赖
npm i use-element-plus-theme
// 或者使用其他包管理器
pnpm i use-element-plus-theme
复制代码
2.引入函数
import { useElementPlusTheme } from "use-element-plus-theme"
复制代码
3.简单使用
const defaultTheme = ref('#405DFF')
const { changeTheme } = useElementPlusTheme(defaultTheme.value)
复制代码
4.完整使用例子
<template>
<el-color-picker v-model="defaultTheme" @change="changeTheme" />
</template>
<script lang="ts" setup>
import { useElementPlusTheme } from 'use-element-plus-theme'
const defaultTheme = ref('#405DFF')
const { changeTheme } = useElementPlusTheme(defaultTheme.value)
</script>
复制代码