基本思路

实现思路:实现一个mixins混入的主题js即theme.js,注册到全局下。使用el-color-picker组件切换颜色的时候,把颜色值传递到根root下,在根实例下监听主题色的变化来更改页面的主题,然后所有具体的路由页面的主题色修改通过在APP.vue页面监听路由变化来调用改变主题色方法。这里面用到providey与inject的使用,以及怎样把设置的主题色传递到根节点下,这里使用了vue1.x中的dispatch方法。

大致总结:

  • 1.把得到的主题色传递给根root实例,在根实例中监听主题色的变化,并调用setThemeColor(newval, oldval)方法;
  • 2.在APP.vue中监听路由变化,并调用setThemeColor(newval, oldval)方法,目的是进入具体路由页面需要修改页面的head中的style样式、DOM元素中的行内style样式;

具体实现如下。

整体效果

先看下整体实现效果:

效果预览地址:《vue+element-ui动态设置主题效果》

使用方式

设置element-ui主题色引入到main.js中

在src/styles下新建element-variables.scss:

/* 改变主题色变量 */
$--color-primary: #42b983; /* 改变 icon 字体路径变量,必需 */
$--font-path: '~element-ui/lib/theme-chalk/fonts'; @import "~element-ui/packages/theme-chalk/src/index"; :export {
colorPrimary: $--color-primary
}

在main.js中引入该css:

import variables from '@/styles/element-variables.scss'

全局混入theme.js、emitter.js

  • theme.js主要方法setThemeColor(newval, oldval),该方法传入新的颜色值与旧的颜色值;
  • emitter.js中使用$$dispatch方法把修改的主题色提交到根实例下;

在main.js 中引入该两个JS并注册:

import theme from '@/mixins/theme.js'
import emitter from '@/mixins/emitter.js' Vue.mixin(theme)
Vue.mixin(emitter)

核心代码调用

  • 主题色提交到根实例代码以及监听具体的路由页面修改样式(APP.vue)
export default {
name: 'App',
inject: {
themeConfig: {
default: () => ({
themeColor: '',
defaultColor: ''
})
}
},
data() {
return {
themeColor: ''
}
},
watch: {
$route() {
// 关键作用-进入到具体路由页面更新页面中DOM样式
if (typeof this.themeConfig.themeColor != 'undefined' && this.themeConfig.themeColor !== this.themeConfig.defaultColor) {
this.$nextTick(() => {
if (this.themeConfig.themeColor && this.themeConfig.defaultColor) {
this.setThemeColor(this.themeConfig.themeColor, this.themeConfig.defaultColor)
}
})
}
}
},
created() {
// 如果本地存在主题色从本地获取,并提交给root分发到页面进行渲染
if(Cookies.get('themeColor')) {
this.themeColor = Cookies.get('themeColor');
this.$$dispatch('root','root.config',[this.themeColor,true]); // 传递数组-解决初始加载执行setThemeColor两次问题
} else {
this.themeColor = this.themeConfig.themeColor;
}
},
methods: {
// 改变主题颜色
changeThemeColor(value) {
this.$$dispatch('root','root.config',value);
Cookies.set('themeColor', value, { path: '/' });
}
}
}
  • 根实例监听获取的主题色并监听设置主题色(main.js)
new Vue({
el: '#app',
name: 'root',
provide(){
return {
themeConfig: this
}
},
data() {
return {
themeColor: variables.colorPrimary.toLowerCase(),
defaultColor: variables.colorPrimary.toLowerCase(),
themeFirstLoaded: true, // 主题是否第一次加载,解决初始主题watch跟$route执行setThemeColor两次问题
}
},
created() {
this.$on('root.config',(result,themeFirstLoaded) => {
this.themeColor = result.toLowerCase();
this.themeFirstLoaded = themeFirstLoaded;
})
},
watch: {
themeColor(newval, oldval) {
if(!this.themeFirstLoaded) {
this.setThemeColor(newval, oldval);
}
}
},
router,
components: { App },
template: '<App/>'
})

theme.js设置主题代码

export default {
methods: {
// 样式更新
updateStyle(stylecon, oldCulster, newCluster) {
let newStyleCon = stylecon;
oldCulster.forEach((color, index) => {
let regexp = '';
if (color.split(',').length > 1) {
const rgbArr = color.split(',');
regexp = new RegExp("\\s*" + rgbArr[0] + "\\s*,\\s*" + rgbArr[1] + "\\s*,\\s*" + rgbArr[2] + "\\s*", 'ig');
} else {
regexp = new RegExp(color, 'ig');
}
newStyleCon = newStyleCon.replace(regexp, newCluster[index])
})
return newStyleCon;
}, // 得到需要修改的一系类颜色值
getThemeCluster(theme) {
const clusters = [theme];
for (let i = 0; i <= 9; i++) {
clusters.push(this.getTintColor(theme, Number(i / 10).toFixed(2)));
}
clusters.push(this.getShadeColor(theme, 0.1));
return clusters;
}, // 得到色调颜色
getTintColor(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((255 - red) * tint);
green += Math.round((255 - green) * tint);
blue += Math.round((255 - blue) * tint);
red = red.toString(16);
green = green.toString(16);
blue = blue.toString(16);
return `#${red}${green}${blue}`
}
}, // 获取阴影色调颜色
getShadeColor(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}`
}, // 获取外链css文本内容
getCSSText(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
const styleText = xhr.responseText.replace(/@font-face{[^}]+}/, '')
resolve(styleText);
}
}
xhr.open('GET', url)
xhr.send()
})
}, // 获取外链CSS样式的url地址
getRequestUrl: function(src) {
if (/^(http|https):\/\//g.test(src)) {
return src;
}
let filePath = this.getFilePath();
let count = 0;
const regexp = /\.\.\//g;
while (regexp.exec(src)) {
count++;
}
while (count--) {
filePath = filePath.substring(0, filePath.lastIndexOf('/'));
}
return filePath + "/" + src.replace(/\.\.\//g, "");
}, // 获取当前window的url地址
getFilePath: function() {
const curHref = window.location.href;
if (curHref.indexOf('/#/') != -1) {
return curHref.substring(0, curHref.indexOf('/#/'));
} else {
return curHref.substring(0, curHref.lastIndexOf('/') + 1);
}
}, // 修改主题色-head样式以及DOM行内样式
async setThemeColor(newval, oldval) {
if (typeof newval !== 'string') return;
const newThemeCluster = this.getThemeCluster(newval.replace('#', ''));
const orignalCluster = this.getThemeCluster(oldval.replace('#', ''));
// 获取原始值中包含rgb格式的值存为数组
const rgbArr = orignalCluster[1].split(',');
const orignalRGBRegExp = new RegExp("\\(\\s*" + rgbArr[0] + "\\s*,\\s*" + rgbArr[1] + "\\s*,\\s*" + rgbArr[2] +
"\\s*\\)", 'i'); // 获取外链的样式内容并替换样式
let styleTag = document.getElementById('new-configTheme__styles');
const tagsDom = document.getElementsByTagName('link');
if (!styleTag && tagsDom.length) {
styleTag = document.createElement('style')
styleTag.setAttribute('id', 'new-configTheme__styles')
document.head.appendChild(styleTag);
const tagsDomList = Array.prototype.slice.call(tagsDom);
let innerTextCon = '';
for (let i = 0; i < tagsDomList.length; i++) {
const value = tagsDomList[i];
const tagAttributeSrc = value.getAttribute('href');
const requestUrl = this.getRequestUrl(tagAttributeSrc);
const styleCon = await this.getCSSText(requestUrl);
if (new RegExp(oldval, 'i').test(styleCon) || orignalRGBRegExp.test(styleCon)) {
innerTextCon += this.updateStyle(styleCon, orignalCluster, newThemeCluster);
}
}
styleTag.innerText = innerTextCon;
} // 获取页面的style标签
const styles = [].slice.call(document.querySelectorAll('style')).filter((style) => {
const text = style.innerText;
return new RegExp(oldval, 'i').test(text) || orignalRGBRegExp.test(text);
}) // 获取页面的style标签内容,使用updateStyle直接更新即可
styles.forEach((style) => {
const {
innerText
} = style;
if (typeof innerText !== 'string') return;
style.innerText = this.updateStyle(innerText, orignalCluster, newThemeCluster);
}) // 获取DOM元素上的style
const domAll = [].slice.call(document.getElementsByTagName('*')).filter((dom, index) => {
const stylCon = dom.getAttribute('style');
return stylCon && (new RegExp(oldval, 'i').test(stylCon) || orignalRGBRegExp.test(stylCon))
})
domAll.forEach((dom) => {
const styleCon = dom.getAttribute('style');
dom.style = this.updateStyle(styleCon, orignalCluster, newThemeCluster);
})
}
}
}

主要思路:通过传入新、旧颜色值替换head标签中样式以及DOM元素style的行内元素的样式。

重要:外链的样式最好是压缩的样式,比如在vue-cli脚手架中,本地开发环境需要把样式提取到一个文件,并且压缩,dev.config.js部分代码如下:

const ExtractTextPlugin = require('extract-text-webpack-plugin') // 提取CSS
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') // 压缩CSS const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap,extract: true, usePostCSS: true })
}, plugins: [
// ...省略其他代码
new ExtractTextPlugin({
filename: 'bundle.css',
allChunks: true
}),
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
})
]
})

github示例源码地址:《vue+element-ui动态主题色设置》

参考地址

给vue+element-ui动态设置主题色(包括外链样式、内联样式、行内样式)的更多相关文章

  1. vue + element ui 实现实现动态渲染表格

    前言:之前需要做一个页面,能够通过表名动态渲染出不同的表格,这里记录一下.转载请注明出处:https://www.cnblogs.com/yuxiaole/p/9786326.html 网站地址:我的 ...

  2. vue+element ui 的tab 动态增减,切换时提示用户是否切换

    前言:工作中用到 vue+element ui 的前端框架,动态添加 Tab,删除 Tab,切换 Tab 时提示用户是否切换等,发现 element ui  有一个 bug,这里记录一下如何实现.转载 ...

  3. 基于vue(element ui) + ssm + shiro 的权限框架

    zhcc 基于vue(element ui) + ssm + shiro 的权限框架 引言 心声 现在的Java世界,各种资源很丰富,不得不说,从分布式,服务化,orm,再到前端控制,权限等等玲琅满目 ...

  4. uni-app 动态修改主题色

    老是碰到初版制作完成没多久,就整一出说什么要更改整个项目的色彩体系.真的是宝宝心里苦啊! 起初都是通过uni项目自带的uni.scss中定义,在替换页面上对应的css.以便于达到一次性修改整体布局的样 ...

  5. vue+element ui 的上传文件使用组件

    前言:工作中用到 vue+element ui 的前端框架,使用到上传文件,则想着封装为组件,达到复用,可扩展.转载请注明出处:https://www.cnblogs.com/yuxiaole/p/9 ...

  6. Vue+Element UI 实现视频上传

    一.前言 项目中需要提供一个视频介绍,使用户能够快速.方便的了解如何使用产品以及注意事项. 前台使用Vue+Element UI中的el-upload组件实现视频上传及进度条展示,后台提供视频上传AP ...

  7. Vue+element ui table 导出到excel

    需求: Vue+element UI table下的根据搜索条件导出当前所有数据 参考: https://blog.csdn.net/u010427666/article/details/792081 ...

  8. Vue+Element的动态表单,动态表格(后端发送配置,前端动态生成)

    Vue+Element的动态表单,动态表格(后端发送配置,前端动态生成) 动态表单生成 ElementUI官网引导 Element表单生成 Element动态增减表单,在线代码 关键配置 templa ...

  9. 分享一个自搭的框架,使用Spring boot+Vue+Element UI

    废弃,新的:https://www.cnblogs.com/hackyo/p/10453243.html 特点:前后端分离,可遵循restful 框架:后端使用Spring boot,整合了aop.a ...

  10. Vue + Element UI 实现权限管理系统

    Vue + Element UI 实现权限管理系统 前端篇(一):搭建开发环境 https://www.cnblogs.com/xifengxiaoma/p/9533018.html

随机推荐

  1. CF372C

    思路 根据题意可以得到dp转移方程是 \(f_{i,j}=\max\{f_{i-1,k}+b_i-|a_i-j|\}\) 而且 \(j-(t_{i}-t_{i-1})\times d\le k\le ...

  2. Java-EL表达式替换和简化jsp页面中java代码的编写

    概念:Expression Language 表达式语言 作用:替换和简化jsp页面中java代码的编写 语法:$ 注意: jsp默认支持el表达式,如果要忽略el表达式 设置jsp中page指令中: ...

  3. 内部网关协议OSPF

    开放最短路径优先OSPF,开放表明OSPF不受某一厂商控制,最短路径优先是因为使用了最短路径算法SPF. OSPF最主要的特征是使用链路状态协议,而不是RIP的距离向量路由协议.其余特点: ⑴使用洪泛 ...

  4. Git 奇幻之旅⌛️

    第一天: 本地仓库 故事的主角是小明,一个刚入门编程的小白.他正在为一个项目写代码,但是他发现每次修改代码都很麻烦,因为他要不断地备份文件,而且很容易弄混版本.有一天,他听说了一个叫 Git 的神奇工 ...

  5. oeasy教您玩转vim - 9 - # 换行插入

    插入新行 回忆上节课内容 上上次是 i.I 在光标前面插入 又加了 a.A 可以在光标后面插入 a 是在光标后插入 A 是在当前行最后插入 关于插入,还有什么命令吗? 我们继续去查阅 help :h ...

  6. 从30个角度对比 PostgreSQL 和 MySQL

    比较版本:PostgreSQL 11    VS      MySQL5.7(innodb引擎) Oracle官方社区版 版权情况:PostgreSQL 11(免费开源).MySQL5.7 Oracl ...

  7. vue高频面试题

    来源:B站程序员来了 第一部分:vue基础 1,v-if和v-for的优先级谁更高?同时出现该如何优化性能? 在同级出现的时候,render函数会将v-for和v-if同时渲染在一个名为_l的函数,在 ...

  8. 错误记录java: JDK isn't specified for module

    跑苍穹外卖的时候遇到了 java: JDK isn't specified for module 'sky-pojo'这一问题 解决办法是通过修改JDK版本,这个项目用的springboot比较早,可 ...

  9. 【VMware】虚拟机 VMware WorkStation Pro 下载安装(Windows)

    官网地址: 下载地址:[VMware WorkStation Pro 15.5 For Windows] https://www.vmware.com/cn/products/workstation- ...

  10. 读论文《基于 GA - BP 的汽车行李箱盖内板冲压成形工艺优化》 —— 如何使用AI技术优化模具产业中工件冲压工艺

    最近到了模具公司工作,本来以为身边同事对模具生产和工件生产的流程(大致流程)会比较了解,结果一问才知道基本都是一问三不知,大家都在模具公司工作但是貌似很多人干的和模具生产和工件制造的工作关联性并不强, ...