美文网首页
【vue】聊一聊Element UI的自定义主题颜色

【vue】聊一聊Element UI的自定义主题颜色

作者: 拖孩 | 来源:发表于2023-06-08 10:25 被阅读0次

    背景

    ElementUI组件库相信大家一定都接触过。但是自定义主题颜色的需求有接触过的应该不多,至少我到今天是没有遇到类似的需求。之所以讲这个需求,是因为在我个人开发的开源项目中有做到这个需求,所以在这里和大家聊一聊我的实现。

    CSS变量

    在此之前我们需要先了解一下CSS变量:它是自定义属性(有时候也被称作CSS变量或者级联变量)是由CSS作者定义的,它包含的值可以在整个文档中重复使用。由自定义属性标记设定值,由var()函数来获取值。

    /* 设定值 */
    :root {
        --background-color: #FF0000;
    }
    /* 获取值 */
    div {
        background-color: var(--background-color);
    }
    

    Element主题色

    Element官方文档我们可以它的整个组件库都是使用的CSS变量实现的。并且它提供了四种方式来修改样式变量,具体方式可以参考官方文档。

    我这边主要介绍的就是Element通过js控制CSS变量设置的方式。那就会有两个问题:

    如何通过js获取到CSS变量

    js获取CSS变量那就要介绍这个方法getComputedStyle():它返回一个对象,该对象在应用活动样式表并解析这些值可能包含的任何基本计算后报告元素的所有 CSS 属性的值。它有两个参数:第一个参数就是用于获取计算样式的Element;第二个参数是可选参数,是指定一个要匹配的伪元素的字符串。返回的style是一个实时的 CSSStyleDeclaration 对象,当元素的样式更改时,它会自动更新本身。CSSStyleDeclaration对象中的getPropertyValue()接口返回一个DOMString,其中包含请求的CSS属性的值。

    const element = document.documentElement;
    const style = window.getComputedStyle(element);
    const value = style.getPropertyValue('`background-color`');
    

    如何通过js设置CSS变量

    js设置CSS变量那就要介绍CSSStyleDeclaration对象中的setProperty()接口,它有三个参数:第一个参数是一个DOMString,代表被更改的CSS属性;第二个参数是一个可选参数也是一个DOMString,含有新的属性值。如果没有指定,则当作空字符串;第三个参数是一个可选参数,是一个 DOMString 允许设置 "important" CSS 优先级。如果没有指定,则当作空字符串。

    有了以上两个方法那我们就可以通过js直接修改Element的主题色了,简单实现一下:

    <template>
      <div class="container">
        <el-button>Default</el-button>
        <el-button v-for="item in types" :key="item" :type="item">{{ item }}</el-button>
        <hr>
        <el-radio-group v-model="radio" @change="radioChangeHandle">
          <el-radio v-for="item in types" :key="item" :label="item" size="large">{{ item }}</el-radio>
        </el-radio-group>
        <br>
        <el-color-picker v-model="color" @change="colorChangeHandle" />
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue'
    
    const el = document.documentElement
    
    const types = ref(['primary', 'success', 'info', 'warning', 'danger'])
    
    const radio = ref('primary') 
    
    const color = ref('')
    
    const radioChangeHandle = () => {
      const value = getComputedStyle(el).getPropertyValue(`--el-color-${radio.value}`)
      color.value = value
    }
    
    const colorChangeHandle = (value) => {
      el.style.setProperty(`--el-color-${radio.value}`, value)
    }
    </script>
    
    <style>
    .container {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -100%);
      width: 50%;
      text-align: center;
    }
    </style>
    
    image.png

    通过以上方式修改了之后,所有的组件的颜色都已经修改了,并且已经达到了效果。但是其中还存在一个问题,以el-button组件举例。每个按钮组件都会有个hover的颜色,虽然显示的颜色已经修改了,但是hover的颜色还是原来的颜色,这是一个严重的Bug。原因是按钮的组件hover的颜色使用了其CSS变量

    image.png image.png

    颜色的HEX格式

    颜色的HEX格式是#+六位数字/字母,其中六位数字/字母是一种十六进制的表达方式。这六位分别两个一组,从左到右分别表示绿00表示最小,十进制是0FF表示最大,十进制是255。通俗点讲,某个颜色的数值越大,包含这个颜色就越多。如:#000000-黑色、#FFFFFF-白色、#FF0000-红色、#00FF00-绿色、#0000FF-蓝色。

    HEX格式颜色变亮、变暗

    通过上文的介绍,我们可以知道Elementhover颜色变亮了,即颜色的数值变大了,那我们只要对要修改的颜色数值变大即可。那就需要用到以下的方法:

    HEX格式转RGB格式

    hex2Rgb(color) {
      color = color.replace('#', '')
      const result = color.match(/../g)
      for (let i = 0; i < 3; i++) {
        result[i] = parseInt(result[i], 16)
      }
      return result
    }
    

    RGB格式转HEX格式

    rgb2Hex(r, g, b) {
      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]
        }
      }
      const result = '#' + hexs.join('')
      return result
    }
    

    使颜色变亮

    lighten(color, level) {
      const rgb = hex2Rgb(color)
      for (let i = 0; i < 3; i++) {
        rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i])
      }
      const result = rgb2Hex(rgb[0], rgb[1], rgb[2])
      return result
    }
    

    使颜色变暗

    darken(color, level) {
      const rgb = hex2Rgb(color)
      for (let i = 0; i < 3; i++) {
        rgb[i] = Math.floor(rgb[i] * (1 - level))
      }
      const result = rgb2Hex(rgb[0], rgb[1], rgb[2])
      return result
    }
    

    解决问题

    有了上文的几个方法,我们就可以在修改主色的时候将对应的其他CSS变量进行变亮或者变暗即可。一般这种主题都是会存储浏览器Storage中,大家可以结合实际情况配合vuex或者pinia使用。这里就不展开聊了。贴上完整代码:

    // utils.js
    export function hex2Rgb(color) {
      color = color.replace('#', '')
      const result = color.match(/../g)
      for (let i = 0; i < 3; i++) {
        result[i] = parseInt(result[i], 16)
      }
      return result
    }
    export function rgb2Hex(r, g, b) {
      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]
        }
      }
      const result = '#' + hexs.join('')
      return result
    }
    export function lighten(color, level) {
      const rgb = hex2Rgb(color)
      for (let i = 0; i < 3; i++) {
        rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i])
      }
      const result = rgb2Hex(rgb[0], rgb[1], rgb[2])
      return result
    }
    export function darken(color, level) {
      const rgb = hex2Rgb(color)
      for (let i = 0; i < 3; i++) {
        rgb[i] = Math.floor(rgb[i] * (1 - level))
      }
      const result = rgb2Hex(rgb[0], rgb[1], rgb[2])
      return result
    }
    // index.vue
    <template>
      <div class="container">
        <el-button>Default</el-button>
        <el-button v-for="item in types" :key="item" :type="item">{{ item }}</el-button>
        <hr>
        <el-radio-group v-model="radio" @change="radioChangeHandle">
          <el-radio v-for="item in types" :key="item" :label="item" size="large">{{ item }}</el-radio>
        </el-radio-group>
        <br>
        <el-color-picker v-model="color" @change="colorChangeHandle" color-format="hex" :show-alpha="false" />
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue'
    
    import { lighten, darken } from '@/utils'
    
    const el = document.documentElement
    
    const types = ref(['primary', 'success', 'info', 'warning', 'danger'])
    
    const radio = ref('primary') 
    
    const color = ref(getComputedStyle(el).getPropertyValue(`--el-color-${radio.value}`))
    
    const radioChangeHandle = () => {
      const value = getComputedStyle(el).getPropertyValue(`--el-color-${radio.value}`)
      color.value = value
    }
    
    const colorChangeHandle = (value) => {
      el.style.setProperty(`--el-color-${radio.value}`, value)
      for (let i = 1; i <= 9; i++) {
        el.style.setProperty(`--el-color-${radio.value}-light-${ i }`, lighten(value, i / 10))
        el.style.setProperty(`--el-color-${radio.value}-dark-${ i }`, darken(value, i / 10))
      }
    }
    </script>
    
    <style>
    .container {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -100%);
      width: 50%;
      text-align: center;
    }
    </style>
    

    以上就是本次分享的内容,附上源码

    感谢看官看到这里,如果觉得文章不错的话,可以给小生的几个开源项目点个Star⭐!

    相关文章

      网友评论

          本文标题:【vue】聊一聊Element UI的自定义主题颜色

          本文链接:https://www.haomeiwen.com/subject/rezoedtx.html