Element-ui 通过实时修改主题色实现一键换肤

2,656 阅读2分钟

Element-ui 是前端日常开发中比较常用的UI库,提供了很多组件给我们使用,但是很多时候我们UI设计稿的颜色和 Element-ui 的不一致, 今天我们来说一下如何去修改 Element-ui 的主题色以及如何实时更新主题色的一键修改主题

其实关于 Element-ui 的修改主题色官方是有介绍的, 点击查看官方说明

// import 'element-ui/lib/theme-chalk/index.css'
import '../theme/index.css'
  • 如果你的项目使用了 scss,那么可以直接新建一个 element-ui 文件在 main.js 中引入即可, 推荐使用这种方式, 方便在项目中随时修改
    /* 改变主题色变量 */ 
    $--color-primary: red; 
    /* 改变 icon 字体路径变量,必需 */ 
    $--font-path: '~element-ui/lib/theme-chalk/fonts'; 
    @import "~element-ui/packages/theme-chalk/src/index";

如果有确认的主题色还是比较简单的, 接下来解释一下如何实现一键换肤这种实时更换主题色,首先扔一个实现好的js, 该js实现了如何实时修改 element-ui 的主题色

const version = require('element-ui/package.json').version // 版本号
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
const ORIGINAL_THEME = '#409EFF' // element-ui 默认的颜色

export default class ThemeColor {
  constructor(defalutColor) {
    const theme = defalutColor || ORIGINAL_THEME
    this.changeThemeColor(theme)
  }
  // 判断是否为颜色
  isColorValue(bgVal) {
    if (bgVal) {
      let type = '^#[0-9a-fA-F]{6}$'
      let re = new RegExp(type)
      if (bgVal.match(re) == null) {
        type =
          '^[rR][gG][Bb][(]([\\s]*(2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?)[\\s]*,){2}[\\s]*(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)[\\s]*[)]{1}$'
        re = new RegExp(type)
        if (bgVal.match(re) == null) {
          return false
        } else {
          return true
        }
      } else {
        return true
      }
    }
  }
  // 更新主题色
  updateStyle(style, oldCluster, newCluster) {
    let newStyle = style
    oldCluster.forEach((color, index) => {
      newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
    })
    return newStyle
  }
  // 改变颜色
  changeThemeColor(colorValue) {
    if (this.isColorValue(colorValue)) {
      const themeCluster = this.getThemeCluster(colorValue.replace('#', ''))
      const originalCluster = this.getThemeCluster(
        ORIGINAL_THEME.replace('#', '')
      )
      const getHandler = (variable, id) => {
        return () => {
          const newStyle = this.updateStyle(
            this[variable],
            originalCluster,
            themeCluster
          )
          let styleTag = document.getElementById(id)
          // 判断是否已经存在标签,没有则生成
          if (!styleTag) {
            styleTag = document.createElement('style')
            styleTag.setAttribute('id', id)
            document.head.appendChild(styleTag)
          }
          // 替换为新的样式表
          styleTag.innerText = newStyle
        }
      }

      const chalkHandler = getHandler('chalk', 'chalk-style')
      // 判断是否已有样式表,没有则根据url请求样式表内容
      if (!this.chalk) {
        this.getCSSString(url, chalkHandler, 'chalk')
      } else {
        chalkHandler()
      }
      const styleDom = document.getElementsByTagName('body')[0].style
      styleDom.setProperty('--theme', colorValue)
    } else {
      console.error('改变主题色出错, 得到的值并不是一个合法的颜色值')
    }
  }
  // 初始化时获取默认主题的样式并复制给this.chalk
  getCSSString(url, callback, variable) {
    const xhr = new XMLHttpRequest()
    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4 && xhr.status === 200) {
        this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
        callback()
      }
    }
    xhr.open('GET', url)
    xhr.send()
  }
  // 获取系列色
  getThemeCluster(theme) {
    const formatColor = (color) => {
      const str = color.toString(16)
      return str.length === 2 ? str : '0' + str
    }

    const tintColor = (color, tint) => {
      let red = parseInt(color.slice(0, 2), 16)
      let green = parseInt(color.slice(2, 4), 16)
      let blue = parseInt(color.slice(4, 6), 16)

      if (tint === 0) {
        return [red, green, blue].join(',')
      } else {
        red += Math.round(tint * (255 - red))
        green += Math.round(tint * (255 - green))
        blue += Math.round(tint * (255 - blue))

        red = formatColor(red)
        green = formatColor(green)
        blue = formatColor(blue)
        return `#${red}${green}${blue}`
      }
    }

    const shadeColor = (color, shade) => {
      let red = parseInt(color.slice(0, 2), 16)
      let green = parseInt(color.slice(2, 4), 16)
      let blue = parseInt(color.slice(4, 6), 16)

      red = Math.round((1 - shade) * red)
      green = Math.round((1 - shade) * green)
      blue = Math.round((1 - shade) * blue)

      red = formatColor(red)
      green = formatColor(green)
      blue = formatColor(blue)
      return `#${red}${green}${blue}`
    }

    const clusters = [theme]
    for (let i = 0; i <= 9; i++) {
      clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
    }
    clusters.push(shadeColor(theme, 0.1))
    return clusters
  }
}


这是用法

const ThemeUI = new ThemeColor('#409EFF')
setTimeout(() => {
  // 改变主题色
  ThemeUI.changeThemeColor('#e50011')
}, 3000)

在修改了 Element-ui 的主题色后,我们还需要修改我们自己使用的一些变量,scss 中我们定义变量时用下面的方式定义

$--theme-color: var(--theme, #409EFF);


// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
  baseColor: $--theme-color;
}

--theme 这是个key, 使用 -- 开头, 接下来我们修改这个变量

const styleDom = document.getElementsByTagName('body')[0].style
styleDom.setProperty('--theme', '#e50011')

这样我们scss定义的变量也可以修改了, 我们在写项目的时候把需要变色的变量通过scss定义,这样就可以实现项目的一键换肤了