基本思路
实现思路:实现一个mixins混入的主题js即theme.js,注册到全局下。使用el-color-picker组件切换颜色的时候,把颜色值传递到根root下,在根实例下监听主题色的变化来更改页面的主题,然后所有具体的路由页面的主题色修改通过在APP.vue页面监听路由变化来调用改变主题色方法。这里面用到providey与inject的使用,以及怎样把设置的主题色传递到根节点下,这里使用了vue1.x中的dispatch方法。
大致总结:
- 1.把得到的主题色传递给根root实例,在根实例中监听主题色的变化,并调用setThemeColor(newval,oldval)方法;
- 2.在APP.vue中监听路由变化,并调用方法,目的是进入具体路由页面需要修改页面的head中的style样式、DOM元素中的行内style样式;
具体实现如下。
整体效果
先看下整体实现效果:
效果预览地址:《vue+element-ui动态设置主题效果》
使用方式
设置element-ui主题色引入到main.js中@H_404_31@
在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@H_404_31@
在main.js 中引入该两个JS并注册:
import theme from '@/mixins/theme.js' import emitter from '@/mixins/emitter.js' Vue.mixin(theme) Vue.mixin(emitter)
核心代码调用@H_404_31@
export default {
name: 'App',inject: {
themeConfig: {
default: () => ({
themeColor: ''
})
}
},data() {
return {
themeColor: ''
}
},watch: {
$route() {
// 关键作用-进入到具体路由页面更新页面中DOM样式
if (typeof this.themeConfig.themeColor != 'undefined' && this.themeConfig.themeColor !== this.themeConfig.defaultColor) {
this.$nextTick(() => {
this.themeConfig.themeColor && .themeConfig.defaultColor) {
this.setThemeColor(this.themeConfig.themeColor,.themeConfig.defaultColor)
}
})
}
}
},created() {
如果本地存在主题色从本地获取,并提交给root分发到页面进行渲染
if(Cookies.get('themeColor')) {
this.themeColor = Cookies.get('themeColor');
this.$$dispatch('root','root.config',[this.themeColor,1)">true]); 传递数组-解决初始加载执行setThemeColor两次问题
} else {
this.themeColor = .themeConfig.themeColor;
}
},methods: {
改变主题颜色
changeThemeColor(value) {
'themeColor',value,{ path: '/' });
}
}
}
new Vue({
el: '#app' {
themeConfig: {
themeColor: variables.colorPrimary.toLowerCase(),defaultColor: variables.colorPrimary.toLowerCase(),themeFirstLoaded: true,1)"> 主题是否第一次加载,解决初始主题watch跟$route执行setThemeColor两次问题
}
},1)">this.$on('root.config',(result,themeFirstLoaded) =>this.themeColor = result.toLowerCase();
this.themeFirstLoaded = themeFirstLoaded;
})
},watch: {
themeColor(newval,oldval) {
if(!.themeFirstLoaded) {
.setThemeColor(newval,oldval);
}
}
},router,components: { App },template: '<App/>'
})
theme.js设置主题代码@H_404_31@
export {
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');
} {
regexp = new RegExp(color,1)">);
}
newStyleCon = newStyleCon.replace(regexp,newCluster[index])
})
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));
clusters;
},1)"> 得到色调颜色
getTintColor(color,tint) {
let red = parseInt(color.slice(0,2),16);
let green = parseInt(color.slice(2,4),1)">);
let blue = parseInt(color.slice(4,6),1)">);
if (tint == 0) {
return [red,green,blue].join(',1)">);
} {
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);
`#${red}${green}${blue}`
}
},1)"> 获取阴影色调颜色
getShadeColor(color,shade) {
let red = parseInt(color.slice(0,1)">);
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 `#${red}${green}${blue}`
},1)"> 获取外链css文本内容
getCSSText(url) {
return new Promise((resolve,reject) => {
const xhr = XMLHttpRequest()
xhr.onreadystatechange = () =>if (xhr.readyState === 4 && xhr.status === 200) {
const styleText = xhr.responseText.replace(/@font-face{[^}]+}/,'')
resolve(styleText);
}
}
xhr.open('GET' 获取外链CSS样式的URL地址
getRequestUrl: function(src) {
if (/^(http|https):\/\//g.test(src)) {
src;
}
let filePath = .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,"");
},1)"> 获取当前window的URL地址
getFilePath: () {
const curHref = window.location.href;
if (curHref.indexOf('/#/') != -1return curHref.substring(0,curHref.indexOf('/#/'));
} {
);
}
},1)"> 修改主题色-head样式以及DOM行内样式
async setThemeColor(newval,1)">typeof newval !== 'string') ;
const newThemeCluster = this.getThemeCluster(newval.replace('#',1)">));
const orignalCluster = this.getThemeCluster(oldval.replace('#',1)"> 获取原始值中包含rgb格式的值存为数组
const rgbArr = orignalCluster[1].split(',1)">);
const orignalRGBRegExp = new RegExp("\\(\\s*" + rgbArr[0] + "\\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 = .getRequestUrl(tagAttributeSrc);
const styleCon = await .getCSSText(requestUrl);
new RegExp(oldval,'i').test(styleCon) || orignalRGBRegExp.test(styleCon)) {
innerTextCon += .updateStyle(styleCon,orignalCluster,newThemeCluster);
}
}
styleTag.innerText = innerTextCon;
}
获取页面的style标签
const styles = [].slice.call(document.querySelectorAll('style')).filter((style) => {
const text = style.innerText;
orignalRGBRegExp.test(text);
})
获取页面的style标签内容,使用updateStyle直接更新即可
styles.forEach((style) => {
const {
innerText
} = style;
typeof innerText !== 'string') ;
style.innerText = .updateStyle(innerText,newThemeCluster);
})
获取DOM元素上的style
const domAll = [].slice.call(document.getElementsByTagName('*')).filter((dom,index) => {
const stylCon = dom.getAttribute('style'return stylCon && ( orignalRGBRegExp.test(stylCon))
})
domAll.forEach((dom) => {
const styleCon = dom.getAttribute('style');
dom.style = 标签中样式以及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 })
},plugins: [
...省略其他代码
ExtractTextPlugin({
filename: 'bundle.css'
}),1)"> OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: false } }
: { safe: }
})
]
})
github示例源码地址:《vue+element-ui动态主题色设置》
参考地址
export default { name: 'App',inject: { themeConfig: { default: () => ({ themeColor: '' }) } },data() { return { themeColor: '' } },watch: { $route() { // 关键作用-进入到具体路由页面更新页面中DOM样式 if (typeof this.themeConfig.themeColor != 'undefined' && this.themeConfig.themeColor !== this.themeConfig.defaultColor) { this.$nextTick(() => { this.themeConfig.themeColor && .themeConfig.defaultColor) { this.setThemeColor(this.themeConfig.themeColor,.themeConfig.defaultColor) } }) } } },created() { 如果本地存在主题色从本地获取,并提交给root分发到页面进行渲染 if(Cookies.get('themeColor')) { this.themeColor = Cookies.get('themeColor'); this.$$dispatch('root','root.config',[this.themeColor,1)">true]); 传递数组-解决初始加载执行setThemeColor两次问题 } else { this.themeColor = .themeConfig.themeColor; } },methods: { 改变主题颜色 changeThemeColor(value) { 'themeColor',value,{ path: '/' }); } } }
new Vue({ el: '#app' { themeConfig: { themeColor: variables.colorPrimary.toLowerCase(),defaultColor: variables.colorPrimary.toLowerCase(),themeFirstLoaded: true,1)"> 主题是否第一次加载,解决初始主题watch跟$route执行setThemeColor两次问题 } },1)">this.$on('root.config',(result,themeFirstLoaded) =>this.themeColor = result.toLowerCase(); this.themeFirstLoaded = themeFirstLoaded; }) },watch: { themeColor(newval,oldval) { if(!.themeFirstLoaded) { .setThemeColor(newval,oldval); } } },router,components: { App },template: '<App/>' })
export { 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'); } { regexp = new RegExp(color,1)">); } newStyleCon = newStyleCon.replace(regexp,newCluster[index]) }) 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)); clusters; },1)"> 得到色调颜色 getTintColor(color,tint) { let red = parseInt(color.slice(0,2),16); let green = parseInt(color.slice(2,4),1)">); let blue = parseInt(color.slice(4,6),1)">); if (tint == 0) { return [red,green,blue].join(',1)">); } { 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); `#${red}${green}${blue}` } },1)"> 获取阴影色调颜色 getShadeColor(color,shade) { let red = parseInt(color.slice(0,1)">); 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 `#${red}${green}${blue}` },1)"> 获取外链css文本内容 getCSSText(url) { return new Promise((resolve,reject) => { const xhr = XMLHttpRequest() xhr.onreadystatechange = () =>if (xhr.readyState === 4 && xhr.status === 200) { const styleText = xhr.responseText.replace(/@font-face{[^}]+}/,'') resolve(styleText); } } xhr.open('GET' 获取外链CSS样式的URL地址 getRequestUrl: function(src) { if (/^(http|https):\/\//g.test(src)) { src; } let filePath = .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,""); },1)"> 获取当前window的URL地址 getFilePath: () { const curHref = window.location.href; if (curHref.indexOf('/#/') != -1return curHref.substring(0,curHref.indexOf('/#/')); } { ); } },1)"> 修改主题色-head样式以及DOM行内样式 async setThemeColor(newval,1)">typeof newval !== 'string') ; const newThemeCluster = this.getThemeCluster(newval.replace('#',1)">)); const orignalCluster = this.getThemeCluster(oldval.replace('#',1)"> 获取原始值中包含rgb格式的值存为数组 const rgbArr = orignalCluster[1].split(',1)">); const orignalRGBRegExp = new RegExp("\\(\\s*" + rgbArr[0] + "\\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 = .getRequestUrl(tagAttributeSrc); const styleCon = await .getCSSText(requestUrl); new RegExp(oldval,'i').test(styleCon) || orignalRGBRegExp.test(styleCon)) { innerTextCon += .updateStyle(styleCon,orignalCluster,newThemeCluster); } } styleTag.innerText = innerTextCon; } 获取页面的style标签 const styles = [].slice.call(document.querySelectorAll('style')).filter((style) => { const text = style.innerText; orignalRGBRegExp.test(text); }) 获取页面的style标签内容,使用updateStyle直接更新即可 styles.forEach((style) => { const { innerText } = style; typeof innerText !== 'string') ; style.innerText = .updateStyle(innerText,newThemeCluster); }) 获取DOM元素上的style const domAll = [].slice.call(document.getElementsByTagName('*')).filter((dom,index) => { const stylCon = dom.getAttribute('style'return stylCon && ( orignalRGBRegExp.test(stylCon)) }) domAll.forEach((dom) => { const styleCon = dom.getAttribute('style'); dom.style = 标签中样式以及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 }) },plugins: [ ...省略其他代码 ExtractTextPlugin({ filename: 'bundle.css' }),1)"> OptimizeCSSPlugin({ cssProcessorOptions: config.build.productionSourceMap ? { safe: false } } : { safe: } }) ] })github示例源码地址:《vue+element-ui动态主题色设置》
参考地址