在网上搜了很多主题切换方案,发现没有适合自己项目的,不得已结合根据实际情况做一个子主题切换的功能。其中参考了
element
官方的theme-chalk-preview
,感兴趣的可以自己研究一下
主要功能是基于
less
切换主题色,可以自定义颜色,同时结合Vuex
和localStorage
对主题色进行缓存,在下次进入项目时初始化
代码地址:theme-chalk-preview代码
预览地址:theme-chalk-preview预览
Vue
的app.vue
文件下,以确保在初始化时能及时获取主题色配置
1. Vuex相关
state: {
theme: localStorage.getItem('theme') || '#409EFF',
originalStyle: ''
}
mutations: {
// 设置主题颜色
setTheme(state, color) {
state.themeColor = color;
localStorage.setItem('themeColor', color);
},
// 获取element 默认样式文件
setStyle(state, data) {
state.originalStyle = data
},
}
getters: {
theme: state => state.theme,
originalStyle: state => state.originalStyle
}
2.新建color工具函数文件(用来替换主题样式文件中的颜色值)
Vuex
中声明了两个变量,然后通过mutations和getters对变量进行获取和赋值
1、theme
,表示当前主题色,由于是存储在localStorage中
的,所以先从localStorage
中获取,然后才是基准色,我的基准色采用的是Element
的默认颜色#409eff
2、originalStyle
,这个变量存放的是element
某个主题颜色的基础样式文件(element-ui的默认样式文件),其实本次主题切换的实质就是在element
的基础样式文件上进行,通过修改该文件中的颜色值来切换主题。放在vuex中的目的是我的需求中有多个地方可以切换主题色,方便获取,以免重复下载样式文件
// util/color.js
import color from 'css-color-function'
// 基于 primary变量 生成颜色键值对
const formula = {
"shade-1": "color(primary shade(10%))",
"light-1": "color(primary tint(10%))",
"light-2": "color(primary tint(20%))",
"light-3": "color(primary tint(30%))",
"light-4": "color(primary tint(40%))",
"light-5": "color(primary tint(50%))",
"light-6": "color(primary tint(60%))",
"light-7": "color(primary tint(70%))",
"light-8": "color(primary tint(80%))",
"light-9": "color(primary tint(90%))"
}
const generateColors = primary => {
let colors = {}
Object.keys(formula).forEach(key => {
// 此处通过传入的primary颜色将formula对象中的值转换成RGB格式的颜色
const value = formula[key].replace(/primary/g, primary)
// 设置RGB颜色值
colors[key] = color.convert(value)
})
return colors
}
/** eg:
const colors = generateColor('#409eff')
colos => {
"shade-1": "rgb(58, 142, 230)",
"light-1": "rgb(83, 168, 255)",
"light-2": "rgb(102, 177, 255)",
"light-3": "rgb(121, 187, 255)",
"light-4": "rgb(140, 197, 255)",
"light-5": "rgb(160, 207, 255)",
"light-6": "rgb(179, 216, 255)",
"light-7": "rgb(198, 226, 255)",
"light-8": "rgb(217, 236, 255)",
"light-9": "rgb(236, 245, 255)"
}
*/
export default generateColors
3.App.vue文件
声明color变量:
data() {
return {
colors: {
primary: "", // 默认颜色
},
};
},
引入generateColors方法:
import generateColors from "@/util/color";
获取vuex中声明的变量
import { mapGetters, mapMutations} from "vuex";
export default {
computed: {
...mapGetters(["themeColor", "originalStyle"]),
},
methods: {
...mapMutations(['setStyle', 'setTheme']),
}
}
下载css文件并替换文件中的颜色值:
export default {
methods: {
...mapMutations(['setStyle', 'setTheme']),
// 该方法是将样式文件中的颜色值替换成键值对中的value(primary、light-n、shade-1)
getStyleTemplate(data) {
// 具体颜色值可参考上面提到的官方案例theme-chalk-preview
const colorMap = {
"#3a8ee6": "shade-1",
"#409eff": "primary",
"#53a8ff": "light-1",
"#66b1ff": "light-2",
"#79bbff": "light-3",
"#8cc5ff": "light-4",
"#a0cfff": "light-5",
"#b3d8ff": "light-6",
"#c6e2ff": "light-7",
"#d9ecff": "light-8",
"#ecf5ff": "light-9",
};
Object.keys(colorMap).forEach((key) => {
const value = colorMap[key];
data = data.replace(new RegExp(key, "ig"), value);
});
return data;
},
// 主方法,可在页面内初始化的时候请求
getIndexStyle() {
// 这里可以根据自己的element版本下载指定版本
this.getFile(
`//unpkg.com/element-ui@2.15.6/lib/theme-chalk/index.css`
).then(({ data }) => {
// 进行颜色值替换,同时将替换后的样式文件保存到vuex中,方便其他地方获取(按你自己的需求来存放)
this.setStyle(this.getStyleTemplate(data));
// 根据传入的themeColor生成颜色键值对
this.colors = Object.assign(
this.colors,
generateColors(this.themeColor)
);
// 生成样式文件插入到页面根节点
this.writeNewStyle();
});
},
// 传入url,下载指定样式文件
getFile(url, isBlob = false) {
return new Promise((resolve, reject) => {
const client = new XMLHttpRequest();
client.responseType = isBlob ? "blob" : "";
client.onreadystatechange = () => {
if (client.readyState !== 4) {
return;
}
if (client.status === 200) {
const urlArr = client.responseURL.split("/");
resolve({
data: client.response,
url: urlArr[urlArr.length - 1],
});
} else {
reject(new Error(client.statusText));
}
};
client.open("GET", url);
client.send();
});
},
}
writeNewStyle() {
// 这里获取到的cssText是将文件中的颜色值替换为(primary、light-n、shade-1)过后的
let cssText = this.originalStyle;
/**
此时this.colors的值为:
{
"primary": "#409eff",
"shade-1": "rgb(58, 142, 230)",
"light-1": "rgb(83, 168, 255)",
"light-2": "rgb(102, 177, 255)",
"light-3": "rgb(121, 187, 255)",
"light-4": "rgb(140, 197, 255)",
"light-5": "rgb(160, 207, 255)",
"light-6": "rgb(179, 216, 255)",
"light-7": "rgb(198, 226, 255)",
"light-8": "rgb(217, 236, 255)",
"light-9": "rgb(236, 245, 255)"
}
然后再将cssText中的(primary、light-n、shade-1)替换成当前主题色的颜色值
*/
Object.keys(this.colors).forEach((key) => {
cssText = cssText.replace(
new RegExp("(:|\s+)" + key, "g"),
"" + this.colors[key]
);
});
// 接下来在dom插入样式文件
let styleTag = document.getElementById("chalk-style");
if (!styleTag) {
styleTag = document.createElement("style");
styleTag.innerText = cssText;
styleTag.setAttribute("id", "chalk-style");
document.head.appendChild(styleTag);
} else {
styleTag.innerText = cssText;
}
this.$store.commit("setTheme", this.colors.primary);
},
},
}
修改成功后可以在head进行查看展开找到我们之前声明id为
chalk-style
的style文件,到这里已经修改完成了这时候如果要更改主题色,只需要修改
color.primary
的值就行了
export default {
created() {
this.colors.primary = this.themeColor;
this.getIndexStyle();
},
}
app.vue完整代码
<template>
<div id="app">
<router-view/>
div>
template>
<script>
import { mapGetters, mapMutations } from "vuex";
import generateColors from "@/util/color";
export default {
data() {
return {
colors: {
primary: "",
},
};
},
computed: {
...mapGetters(["themeColor", "originalStyle"]),
},
created() {
this.colors.primary = this.themeColor;
this.getIndexStyle();
},
methods: {
...mapMutations(['setStyle', 'setTheme']),
getStyleTemplate(data) {
const colorMap = {
"#3a8ee6": "shade-1",
"#409eff": "primary",
"#53a8ff": "light-1",
"#66b1ff": "light-2",
"#79bbff": "light-3",
"#8cc5ff": "light-4",
"#a0cfff": "light-5",
"#b3d8ff": "light-6",
"#c6e2ff": "light-7",
"#d9ecff": "light-8",
"#ecf5ff": "light-9",
};
Object.keys(colorMap).forEach((key) => {
const value = colorMap[key];
data = data.replace(new RegExp(key, "ig"), value);
});
return data;
},
getIndexStyle() {
this.getFile(
`//unpkg.com/element-ui@2.15.6/lib/theme-chalk/index.css`
).then(({ data }) => {
this.setStyle(this.getStyleTemplate(data))
this.colors = Object.assign(
this.colors,
generateColors(this.themeColor)
);
this.writeNewStyle();
});
},
getFile(url, isBlob = false) {
return new Promise((resolve, reject) => {
const client = new XMLHttpRequest();
client.responseType = isBlob ? "blob" : "";
client.onreadystatechange = () => {
if (client.readyState !== 4) {
return;
}
if (client.status === 200) {
const urlArr = client.responseURL.split("/");
resolve({
data: client.response,
url: urlArr[urlArr.length - 1],
});
} else {
reject(new Error(client.statusText));
}
};
client.open("GET", url);
client.send();
});
},
writeNewStyle() {
let cssText = this.originalStyle;
Object.keys(this.colors).forEach((key) => {
cssText = cssText.replace(
new RegExp("(:|\s+)" + key, "g"),
"" + this.colors[key]
);
});
let styleTag = document.getElementById("chalk-style");
if (!styleTag) {
styleTag = document.createElement("style");
styleTag.innerText = cssText;
styleTag.setAttribute("id", "chalk-style");
document.head.appendChild(styleTag);
} else {
styleTag.innerText = cssText;
}
this.setTheme(this.colors.primary)
},
},
};
script>
<style lang="less">
style>
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)