Vue + ElementUI 实现动态更换任意主题色(动态换肤)

Vue + Element UI+Scss + Vuex一键换肤 , 一键换字体大小 ,动态替换全局主题颜色 一、前言 其实我这个写法每个UI库都通用 , 不局限于ElementUI , 看明白思路就知道怎么写了 一键换肤 , 动态替换全局主题颜色功能已经实现很久了 , 在项目验收的时候出现了一个小问题 , 想改动一下 , 于是来记录一下 前段时间公司项目里需要实现一键换服 , 换主题颜色 , 网上看了一部分 大多数都是用js获取标签元素添加Attribute , 如document.XXXXXXX ,或者是建theme.css文件什么的 , 不太符合我想要实现的用最少的代码实现换肤的功能 , 如果用了框架 , 还 阅读详情
AI摘要

本文介绍了在Vue项目中基于ElementUI实现动态换肤的两种方法:vue-element-admin官方方式和webpack-theme-color-replacer插件。官方方式主要通过修改SCSS变量并动态添加style标签来覆盖原有样式,而插件方式则是通过替换CSS中的颜色值。文章详细阐述了每种方法的实现步骤,包括创建自定义组件、使用Vuex管理和存储主题色、以及处理刷新后主题色的保持问题。

最近基于 ElementUI 的项目上需要实现动态换肤的功能,这里提供两种方式:

  1. vue-element-admin 官方实现的方式
  2. webpack-theme-color-replacer 插件的实现方式

vue-element-admin 官方实现的方式

简单说明一下它的原理: element-ui 2.0 版本之后所有的样式都是基于 SCSS 编写的,所有的颜色都是基于几个基础颜色变量来设置的,所以就不难实现动态换肤了,只要找到那几个颜色变量修改它就可以了。 首先我们需要拿到通过 package.json 拿到 element-ui 的版本号,根据该版本号去请求相应的样式。拿到样式之后将样色,通过正则匹配和替换,将颜色变量替换成你需要的,之后动态添加 style 标签来覆盖原有的 css 样式。

  1. 这里无脑参考 vue-element-admin 的源码,首先现在 styles 文件夹下创建一个名为 element-variables.scss 文件,并在 main.js 中引入该文件。

    /* src/styles/element-variables.scss */
    
    /* 改变主题色变量 */
    $--color-primary: #256DCB;
    $--color-success: #7AC800;
    $--color-danger: #EC4242;
    $--color-info: #999999;
    
    /* 改变 icon 字体路径变量,必需 */
    $--font-path: '~element-ui/lib/theme-chalk/fonts';
    
    @import "~element-ui/packages/theme-chalk/src/index";
    
    /* 如遇到导出的变量值为 undefined 则本文件名需要改成 element-variables.module.scss */
    :export {
      theme: $--color-primary
    }
    
    /* main.js */
    
    import Vue from 'vue'
    import Element from 'element-ui'
    import './element-variables.scss'
     
    Vue.use(Element)
    
  2. 通过以上方式已经能够实现 ElementUI 主题替换了,但是想要做到自定义颜色还是远远不够的,考虑到主题色在整个项目中都有应用,所以我们将它存到 Vuex 中,接下来要做的就是在 store/modules 下新建 settings.js 。

    /* src/store/modules/settings.js */
    
    import variables from '@/styles/element-variables.module.scss'
    import * as Types from '../mutation-types'
    
    const state = {
      theme: variables.theme
    }
    
    const mutations = {
      CHANGE_SETTING: (state, { key, value }) => {
        localStorage.setItem('theme', value) // 缓存起来,刷新的时候重新取用
        // eslint-disable-next-line no-prototype-builtins
        if (state.hasOwnProperty(key)) {
          state[key] = value
        }
      }
    }
    
    const actions = {
      changeSetting ({ commit }, data) {
        commit('CHANGE_SETTING', data)
      }
    }
    
    export default {
      namespaced: true,
      state,
      mutations,
      actions
    }
    
  3. 这一步也是无脑 copy vue-element-admin 的源码,新建一个名为 ThemePicker 的组件,代码如下:

    /* src/components/ThemePicker/index.vue */
    
    <template>
      <el-color-picker
        v-model="theme"
        :predefine="['#256DCB', '#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', '#F80', ]"
        class="theme-picker"
        popper-class="theme-picker-dropdown"
      />
    </template>
    
    <script>
    
    import { chalkCss } from './chalk.js' // 就是 getCSSString 接口中获取的 css,这里我将版本号固定并且把 css 缓存到本地,记得处理 \ 转义问题
    
    // const version = require('element-ui/package.json').version // element-ui version from node_modules
    const ORIGINAL_THEME = '#409EFF' // default color  chalk.js 中的 primary color,根据此颜色计算出一系列颜色进行正则替换
    
    export default {
      name: 'ThemePicker',
      data () {
        return {
          chalk: '', // content of theme-chalk css
          theme: ''
        }
      },
      computed: {
        defaultTheme () {
          return this.$store.state.settings.theme
        }
      },
      watch: {
        defaultTheme: {
          handler: function (val, oldVal) {
            this.theme = val
          },
          immediate: true
        },
        async theme (val) {
          const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
          if (typeof val !== 'string') return
          const themeCluster = this.getThemeCluster(val.replace('#', ''))
          const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
          console.log(themeCluster, originalCluster)
    
          const $message = this.$message({
            message: this.$t('theme_compiling'),
            customClass: 'theme-message',
            type: 'success',
            duration: 0,
            iconClass: 'el-icon-loading'
          })
    
          const getHandler = (variable, id) => {
            return () => {
              const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
              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
            }
          }
    
          if (!this.chalk) {
            // const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
            // await this.getCSSString(url, 'chalk')
            this.chalk = chalkCss.replace(/@font-face{[^}]+}/, '') // 本地缓存,如果需要获取线上的就用上面那种方式,优点:切换无延迟,缺点:需要手动维护 css string
          }
    
          const chalkHandler = getHandler('chalk', 'chalk-style')
    
          chalkHandler()
    
          const styles = [].slice.call(document.querySelectorAll('style'))
            .filter(style => {
              const text = style.innerText
              return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
            })
          styles.forEach(style => {
            const { innerText } = style
            if (typeof innerText !== 'string') return
            style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
          })
    
          this.$emit('change', val)
    
          $message.close()
        }
      },
    
      methods: {
        updateStyle (style, oldCluster, newCluster) {
          let newStyle = style
          oldCluster.forEach((color, index) => {
            newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
          })
          return newStyle
        },
    
        getCSSString (url, variable) {
          return new Promise(resolve => {
            const xhr = new XMLHttpRequest()
            xhr.onreadystatechange = () => {
              if (xhr.readyState === 4 && xhr.status === 200) {
                this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
                resolve()
              }
            }
            xhr.open('GET', url)
            xhr.send()
          })
        },
    
        getThemeCluster (theme) {
          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) { // when primary color is in its rgb space
              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 = red.toString(16)
              green = green.toString(16)
              blue = blue.toString(16)
    
              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 = red.toString(16)
            green = green.toString(16)
            blue = blue.toString(16)
    
            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
        }
      }
    }
    </script>
    
    <style>
    .theme-message,
    .theme-picker-dropdown {
      z-index: 99999 !important;
    }
    
    .theme-picker .el-color-picker__trigger {
      height: 26px !important;
      width: 26px !important;
      padding: 2px;
    }
    
    .theme-picker-dropdown .el-color-dropdown__link-btn {
      display: none;
    }
    </style>
    
  4. 最后在页面中使用 ThemePicker 组件,我这里是放到 header 里。

    // template
    <theme-picker @change="themeChange" />
    // js
    import ThemePicker from '@/components/ThemePicker'
    // methods
    themeChange (val) {
      this.$store.dispatch('settings/changeSetting', {
        key: 'theme',
        value: val
      })
    }
    

到这里为止就已经实现了全部动态换肤的功能,不过还有一个问题就是刷新页面之后,主题色会重置,所以在切换的时候需要将主题色缓存到 localStore 中去,为了在任意页面刷新都可以加载主题色,我选择在 App.vue 页面也增加一个 ThemePicker 组件,具体实现见如下代码:

/* src/App.vue */

<template>
  <div id="app" :style="{'--color': defaultTheme}">
    <theme-picker @change="themeChange" v-show="false" />
    <router-view />
  </div>
</template>

<script>
import ThemePicker from '@/components/ThemePicker'

export default {
  name: 'App',
  components: { ThemePicker },
  computed: {
    defaultTheme () {
      return this.$store.state.settings.theme
    }
  },
  mounted () {
    if (localStorage.getItem('theme')) {
      this.themeChange(localStorage.getItem('theme'))
    }
  },
  methods: {
    themeChange (val) {
      this.$store.dispatch('settings/changeSetting', {
        key: 'theme',
        value: val
      })
    }
  }
}
</script>

另外一些自己定义的样式如果需要用到主题色可以用 css 变量,但需要预先在 root 上定义好变量,我这里使用 vue 双向绑定将主题色绑定到 :style="{'--color': defaultTheme}" (具体用法见上面代码),这样就能在任何组件下使用该主题色了(注意:该方法 IE 浏览器不支持),代码如下:

<div class="box"></div>
.box {
  width: 100px;
  height: 100px;
  background-color: var(--color);
}

提示:

  1. 如果 scss 文件导出的是空对象,则需要将 scss 文件名称改成 xxx.module.scss 的形式,CSS Modules。
  2. 如果需要缓存 chalkCss 的,css string 中的 \ 会被转义,记得手动改成 \\,不然 ElementUI 自带的 icon 展示不出来。

参考文档:

webpack-theme-color-replacer 插件的实现方式

此方式亲测可用,本质上也是颜色的替换,但是跟 ElementUI 稍稍有点兼容性问题,具体实现方式见 webpack-theme-color-replacer 的使用,我这里就不做过多赘述了。

Vue+ElementUI实现在线动态换肤 实现的原理:版本之后所有的样式都是基于SCSS编写的,所有的颜色都是基于几个基础颜色变量来设置的,所以就不难实现动态换肤了,只要找到那几个颜色变量修改它就可以了。首先我们需要拿到通过拿到element-ui的版本号,根据该版本号去请求相应的样式。拿到样式之后将样色,通过正则匹配和替换,将颜色变量替换成你需要的,之后动态添加style标签来覆盖原有的css样式。 阅读详情

相关推荐

Vue+ElementUI 切换ElementUI主题+自定义颜色的修改(指定几种颜色的换肤)

一、版本说明(若没有以下中间件,先安装喔) 1.1、具体代码开源到github中,欢迎star Node.js 10.13.0 NPM 6.10.3 Vue 2.5.2 vue-router 3.0.1 element-ui 2.11.1 element-theme-chalk 2.11.1...

窝窝头的博客 2万+

element-ui修改主题颜色

在使用饿了么组件时,往往设计稿和组件自带的主题颜色不一致,如果手动将每一个按钮,tag标签等的颜色进行修改,工作量很大,所以有必要对主题颜色进行统一的规范,从而提高效率。

weixin_62918410的博客 9743

vue ---- 仿ElementUI自定义换肤,更换主题

<template> <div> <input type="color" id='selectTheme' v-model="theme"> <script type='text/template' id="themeColorInfo"> .theme-color { color: {{theme}}...

studentenglish的博客 2169

修改element-ui主题颜色

element-ui 修改主题颜色 vue 前端

Zwq8023520的博客 6728

Vue - 超详细 Element 组件库主题颜色进行 “统一全局“ 替换,将默认的蓝色主题色更换为其他自定义颜色(保姆级教程,简易且标准全局替换主题色)

vueElement主题色如何更改,vueelement-ui修改主题颜色,vue改变elementui的主题色,vue全局修改element-ui的主题颜色,vue项目更改element-ui主题色,vue全局更改饿了么element组件库主题色,在vue项目中改变element的主题色,vue全局修改element-ui的主题颜色,vue+ElementUI实现动态更换任意主题色,vueelementui实现更换主题颜色,vue怎么把element组件库的默认的蓝色主题改成其他颜色,vue

🏆 前端领域优质创作者 8015

从驱动编译到数据传输:RK3588与FPGA的PCIe通信实战解析

本文详细解析了RK3588与FPGA通过PCIe通信的实战过程,涵盖驱动编译、数据传输及性能优化等关键步骤。通过XDMA驱动实现高效DMA传输,适用于视频处理、数据采集等高性能场景,帮助开发者快速掌握嵌入式系统中的PCIe通信技术。

weixin_29214691的博客 528

Vue+ElementUI实现动态更换任意主题色(动态换肤)的全过程

Vue+ElementUI实现动态更换任意主题色(动态换肤)的全过程

qq_43432157的博客 4414

高效静音黑科技 —— 单相直流无刷电机驱动器

然而,传统的BLDC电机驱动通常依赖于三相架构,电路复杂,成本高昂,且在低功率应用中显得有些“大材小用”。单相直流无刷电机驱动器作为一项高效、静音、经济的技术,正逐渐改变着各个领域的电机驱动方式。其精巧的控制策略、显著的优势以及广阔的应用前景,都预示着这项技术将在未来扮演越来越重要的角色。它相较于传统的三相驱动,显著简化了电路结构,降低了成本,尤其适用于低功率、对静音要求极高的应用场景。通过这些模块的协同工作,单相直流无刷电机驱动器能够实现对电机速度、扭矩的精确控制,并最大程度地降低噪声和振动。

wen8789的博客 1766

基于Vue、Element的换肤

基于Vue、Element的换肤方案一 本文将介绍几种基于Vue、Element-UI的换肤实现方案,力争通俗易懂,易上手,希望大家喜欢~ 方案一 使用全局的样式

热爱思考、活力满满、开朗爱笑的年轻人 6374

遗传算法优化光学超表面:智能设计突破纳米结构瓶颈

光学超表面设计本质上是一个高维、非线性、离散每个结构单元(如纳米砖柱、孔洞、十字架)有多个参数(长、宽、高、旋转角、材料),整个阵列的参数组合数量随单元数指数级增长。性能指标(如聚焦效率、工作带宽、偏振转换率、特定角度响应)往往无法用简单解析式表达,需依赖计算密集型电磁仿真(如FDTD, FDFD, RCWA)来评估。结构参数微小变化可能引起光学响应的剧烈跃变,目标函数存在多个局部最优解。实际制造通常要求结构参数取自有限的离散值(如特定刻蚀深度、材料库)。

weixin_51101456的博客 1143

实例讲解Simulink应用层开发CAN报文解包及CAN信号设置方法

本文通过讲解MCU CAN解包模块的模块设置、输入输出信号的设置及仿真测试验证,说明了CAN解包模块的设置方法、CAN解包模块输入输出信号的设置方法以及CAN解包模块的仿真测试及修正方法。在模型搭建过程中,建议养成一种搭建完成仿真测试验证的习惯,在模块阶段发现错误,解决错误,避免在后期模块集成之后报出大量错误,增加解决错误的工作量和难度。希望通过本文的介绍能给相关技术人员带来一些参考和帮助。

qq_43011586的博客 5682

Vue + ElementUI 动态更换皮肤主题色,样式保存至本地(记录踩坑:图标失效、颜色只第一次生效、衍生色未动态改变)

vue+elementui动态更换主题肤色,样式保存至本地。通过接口获取elementui线上的theme-chalk样式修改主题色,非常不稳定,访问速度慢,今天一看还挂了,所以必须保存到本地才行,记录其中遇到的一些问题。

weixin_45701630的博客 1465

变邻域搜索(VNS)原理梳理和应用细节-附求解VRPTW问题C++代码

文章目录1、简介-introduction2、VNS的基本机制2.1 变邻域下降方法-Variable Neighborhood Descent2.2基本变邻域搜索方法-Basic Variable Neighborhood Search2.3 终止准则3、VNS的拓展和混杂算法3.1 VNS的拓展3.2 变邻域分解搜索-Variable Neighborhood Decomposition Search3.3 偏变邻域搜索-Skewed VNS3.4 并行变邻域搜索-Parallel VNS4、VNS和其

dragon Fly的博客 6826

vue 实现主题换肤(element-ui)

站在巨人的肩膀上开发之主题换肤 一. 插件安装 https://www.npmjs.com/package/webpack-theme-color-replacer npm i webpack-theme-color-replacer -D 二. 项目使用 2.1 vue.config.js const webpack = require('webpack') const ThemeCo...

YuShiYue的博客 1286

五种音频器件综合对比——《器件手册--音频器件》

音频器件当在蜂鸣片的两片电极之间接通交流音频信号时,蜂鸣片会根据信号的大小和频率发生振动,从而产生相应的声音。结构简单、造价低廉。广泛应用于玩具、电子表、电子仪器、电子钟表、定时器等电子电器方面。蜂鸣器是一种一体化结构的电子讯响器,采用直流电源供电,广泛应用于各种电子产品中作发声器件有源蜂鸣器:内部带震荡源,通电即发声,程序控制方便。无源蜂鸣器:内部不带震荡源,需用方波驱动,声音频率可控,价格相对便宜。计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品。

XU157303764的博客 1537

FATFS挂载多个存储设备(2):功能配置及功能测试

本文介绍了基于FATFS文件系统实现多存储设备挂载的方法,包含底层设备驱动函数编写、功能配置、功能测试。

kevin1499的博客 2585

vue切换主题

本文介绍两种elementUI切换主题色的方法 项目示例:https://github.com/yangzm0618/tags 1:官方提供的方法,直接修改scss文件达到修改目的,开发人员使用(比官网容易理解,有图有真相)。 2:通过选颜色值在项目中达到换肤目的,用户使用。此方法根据官方主题切换修改而来 先搭建项目:   npm install -g vue//全局安装vue ...

CecePony的博客 3758

3. NVIDIA GPU Xid 常见错误码全解析、故障定位与标准化处理方案

摘要: Xid是NVIDIA显卡驱动记录的硬件/软件异常日志,通过dmesg等工具可获取,包含PCI总线、GPU编号、故障码等信息,是诊断显卡问题的核心依据。Xid故障分为四类:应用程序类(如代码错误,Xid13/31)、显存ECC硬件类(如显存损坏,Xid48/63)、链路/供电温控类(如PCIe故障,Xid74/79)和GSP固件类(如静电干扰,Xid119/154)。排查流程包括日志采集、软件修复(如重启进程、升级驱动)、硬件检查(如PCIe链路状态)及断电复位(针对GSP故障)。预防措施包括监控EC

jundao1997的专栏 707

vue-基于elementui自定义主题更换皮肤及自定义内容的皮肤跟换

vue-基于elementui自定义主题更换皮肤及自定义内容的皮肤跟换 参考这篇博客https://blog.csdn.net/young_Emily/article/details/78591261做一遍,加上自己的一些理解 思路:通过自己上一篇博客https://www.cnblogs.com/aidixie/p/10309862.html做出...

qq_35859392的博客 4146

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

vue3.x自定义换肤

可以使用elementUI官网给出的方案多个皮肤之间的换肤 使用插件进行自定义换肤

weixin_43443341的博客 1730

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

vue 主题更换 elemeny-ui下动态切换主题色

vue 主题更换 elemeny-ui下动态切换主题色 一、文章学习来源 https://www.cnblogs.com/rogerwu/p/9542165.html 二、在这里插入代码片 <template> <el-color-picker class="theme-picker" popper-class="theme-picker-dropdown" v-model="theme"></el-color-picker> </tem

zhangquanming123的博客 619

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Vue+Less/Scss实现主题切换功能

目前,在众多的后台管理系统中,换肤功能已是一个很常见的功能。用户可以根据自己的喜好,设置页面的主题,从而实现个性化定制。目前,我所了解到的换肤方式,也是我目前所掌握的两种换肤方式,想同大家一起分享。

hbmern的博客 3198

vue项目中实现动态换肤

换肤是一个常见的需求,换肤涉及的面很多,总的涉及到的有自定义的组件样式,外部引入的组件(element,百度地图等等),但其实只需要我们分模块去解决,就能迎刃而解。

nick_zhang的博客 1111

Vue + Element UI 前端篇(六):更换皮肤主题

首先安装「主题生成工具」,可以全局安装或者安装在当前项目下,推荐安装在项目里,方便别人 clone 项目时能直接安装依赖并启动。把生成的主题按颜色改名放置 src/theme 目录下。在 main.js 中 import ‘所在路径/index.css’。

y19910825的博客 7073

Element-UI动态更换主题

参考:vue-基于elementui换肤[自定义主题] 实践: 需求1.后期维护主题色不更换:   直接在线主题生成工具下载,在APP.VUE引入;(注意Element UI 版本1.3?2.0)    需求2.后期维护主题色频繁更换:   需要两个工具: 步骤1.element-variables.scss 可以设置自定义主题(按钮颜色需求、根字体大小等...

weixin_30265171的博客 3699
上一篇: Echarts实现“暂无数据”的几种方案
趣果有间
博客等级 码龄10年 15粉丝 17原创
评论 19
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值