TypescriptelementUIreact & vue & angular

Vue3+ElementPlus+TS 动态更换主题

2022-11-20  本文已影响0人  硅谷干货

一、思路分析

  1. 网上有的修改主题的思路都是自定义样式文件,全局引入,动态修改根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)
    复制代码
    
  2. 打开

    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>
复制代码

四、源码以及预览

源码地址

上一篇下一篇

猜你喜欢

热点阅读