自定义主题色包含以下两种,并且这两种的主题色需要同步
- element-ui的主题色
- 程序员自己写的样式中的主题色
我参考
- 一篇博客:vue,elementUI切换主题,自定义主题 - 小兔额乖乖 - 博客园
- 一个开源框架:腾讯的tDesign搭建的开源框架——TDesign Vue Next Starter
输出了此篇文章
说明1:小兔额乖乖文章中的项目示例,在修改主题色后,刷新一下,鼠标hover时会有问题,此问题在本篇文章中已经解决
说明2:我曾经尝试完全模仿腾讯那个框架的思路。但是此思路不适用于element-ui这个框架,会报错
说明3:下面的正文中,项目中修改主题色时,会运行 document.head.appendChild(style) ,修改一次颜色就会添加一个stylesheet,有兴趣的同学可以优化一下,改成替换掉上一次的style。
以下是正文
---------------------------------------------------------------------------------------------------------------------------------
1、创建一个vue项目
① vue-cli3创建项目
vue create vue-custom-theme
② 安装element-ui相关
安装elementUI
npm i element-ui -S
安装sass
npm install node-sass@4.11.0 sass-loader@7.1.0 -D
启动项目
npm run serve
2、引入element-ui
①在main,js中
import ElementUI from 'element-ui'
import './styles.scss'
Vue.use(ElementUI)
②给一个默认的主题色【此步骤可省略,不需要。因为可以在Theme.vue中直接从后台获取默认主题(this.$store.state.sysConfig.theme)。this.$store.state.sysConfig.theme是在后台获取的,存储在了vuex里面,获取时机是全局路由拦截,在进入页面之前获取的】
用户先从线上拉去配置主题的css文件,当用户更改颜色后,在把css文件里面所有的颜色值替换掉,然后把这个css文件重新插入到html中达到改变颜色。
在这里都需要修改再方法1的基础上进行扩展:在element-variables.scss(大部分项目都是)添加 默认我们自己设置的颜色。
当然这个颜色也可以在其他公共css修改。我是在style.scss中修改的,如下
style.scss
/* theme color */
$--color-primary: #ff6f4b;/* icon font path, required */
$--font-path: '~element-ui/lib/theme-chalk/fonts';@import "~element-ui/packages/theme-chalk/src/index";
3、主题色修改
①安装两个插件
npm install css-color-function npm install object-assign
②新建utils文件夹。如下图
color.js 文件内容
import color from 'css-color-function'
import formula from './formula.json'const generateColors = (type,colorval)=> {let colors = {}Object.keys(formula).forEach(key => {const value = formula[key].replace(/primary/g, colorval)colors[type+""+key] = color.convert(value)})console.log(colors)return colors
}export default generateColors
formula.json文件内容:
{"s-1": "color(primary shade(10%))","l-1": "color(primary tint(10%))","l-2": "color(primary tint(20%))","l-3": "color(primary tint(30%))","l-4": "color(primary tint(40%))","l-5": "color(primary tint(50%))","l-6": "color(primary tint(60%))","l-7": "color(primary tint(70%))","l-8": "color(primary tint(80%))","l-9": "color(primary tint(90%))"
}
③ 从 unpkg.com/element-ui/lib/theme-chalk/index.css 把最新css文件复制下来copy到项目静态文件目录中:
④ 接下来就是写代码了。在App.vue上引入自定义的修改主题组件,再随便弄些element组件观察变化:
⑤写组件:Theme.vue
<!-- 切换主题色 -->
<template><div class="handle-theme"><el-color-picker@change="colorChange"v-model="colors.primary"></el-color-picker></div>
</template>
<script>
import generateColors from "@/utils/color";
import objectAssign from 'object-assign';
export default {name: 'App',data() {return {originalStylesheetCount: -1, // 记录当前已引入style数量originalStyle: '', // 获取拿到的.css的字符串colors: {primary: '#ff6f4b',// this.$store.state.sysConfig.theme,success: '#00c5bd',warning: '#f39800',danger: '#f56c6c',info: '#909399'},// primaryColor: "", //提交成功后设置默认颜色cssUrl: ['//unpkg.com/element-ui/lib/theme-chalk/index.css',process.env.BASE_URL + 'static/css/index.css']};},methods: {initTheme() {// 默认从线上官方拉取最新css,2秒钟后做一个检查没有拉到就从本地在拉下// 如果是记住用户的状态就需要,在主题切换的时候记录颜色值,在下次打开的时候从新赋值this.colors.primary = localStorage.getItem('color') || this.colors.primary; // 例如// this.getIndexStyle(this.cssUrl[0]);// setTimeout(() =>{// if (this.originalStyle) {// return;// } else {this.getIndexStyle(this.cssUrl[1]);// }// }, 2000);this.$nextTick(() => {// 获取页面一共引入了多少个style 文件this.originalStylesheetCount = document.styleSheets.length;});},colorChange(e) {if (!e) return;this.primaryColor = e;localStorage.setItem('color', e);let primarySeries = this.generateColors('p', e)let successSeries = this.generateColors('s', this.colors.success)let warningSeries = this.generateColors('w', this.colors.warning)let dangerSeries = this.generateColors('d', this.colors.danger)let infoSeries = this.generateColors('i', this.colors.info)this.colors = objectAssign({},this.colors,primarySeries,successSeries,warningSeries,dangerSeries,infoSeries);this.writeNewStyle();},writeNewStyle() {let cssText = this.originalStyle;Object.keys(this.colors).forEach((key) => {cssText = cssText.replace(new RegExp('(:|\\s+)' + key, 'g'),'$1' + this.colors[key]);});const style = document.createElement('style');style.innerText = `:root{--primary-color: ${this.colors.primary};--primary-color-light-1:${this.colors['pl-1']};--primary-color-light-2:${this.colors['pl-2']};--primary-color-light-3:${this.colors['pl-3']};--primary-color-light-4:${this.colors['pl-4']};--primary-color-light-5:${this.colors['pl-5']};--primary-color-light-6:${this.colors['pl-6']};--primary-color-light-7:${this.colors['pl-7']};--primary-color-light-8:${this.colors['pl-8']};--primary-color-light-9:${this.colors['pl-9']};}${cssText}`;// ":root{--primary-color:" + this.colors.primary + "}" + cssText;document.head.appendChild(style);// if (this.originalStylesheetCount === document.styleSheets.length) {// // 如果之前没有插入就插入// const style = document.createElement("style");// style.innerText =// ":root{--primary-color:" + this.colors.primary + "}" + cssText;// document.head.appendChild(style);// } else {// // 如果之前没有插入就修改// document.head.lastChild.innerText =// ":root{--primary-color:" +// this.colors.primary +// "} " +// cssText;// }},getIndexStyle(url) {var request = new XMLHttpRequest();request.open('GET', url);request.onreadystatechange = () => {if (request.readyState === 4 &&(request.status == 200 || request.status == 304)) {// 调用本地的如果拿不到会得到html,html是不行的if (request.response && !/DOCTYPE/gi.test(request.response)) {this.originalStyle = this.getStyleTemplate(request.response);this.colorChange(this.colors.primary);} else {this.originalStyle = '';}} else {this.originalStyle = '';}};request.send(null);},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",'#3a8ee6': 'ps-1','#409eff': 'primary','#53a8ff': 'pl-1','#66b1ff': 'pl-2','#79bbff': 'pl-3','#8cc5ff': 'pl-4','#a0cfff': 'pl-5','#b3d8ff': 'pl-6','#c6e2ff': 'pl-7','#d9ecff': 'pl-8','#ecf5ff': 'pl-9','#5daf34': 'ss-1', // is-plain:active'#67c23a': 'success', // success'#76c84e': 'sl-1', // 没有'#85ce61': 'sl-2', // hover color'#95d475': 'sl-3', // 没有'#a4da89': 'sl-4', // is-disabled hover color'#b3e19d': 'sl-5', // is-disabled color'#c2e7b0': 'sl-6', // border-color'#d1edc4': 'sl-7', // 没有'#e1f3d8': 'sl-8', // light'#f0f9eb': 'sl-9', // lighter'#cf9236': 'ws-1', // is-plain:active'#E6A23C': 'warning', // success'#e9ab50': 'wl-1', // 没有'#ebb563': 'wl-2', // hover color'#eebe77': 'wl-3', // 没有'#f0c78a': 'wl-4', // is-disabled hover color'#f3d19e': 'wl-5', // is-disabled color'#f5dab1': 'wl-6', // border-color'#f8e3c5': 'wl-7', // 没有'#faecd8': 'wl-8', // light'#fdf6ec': 'wl-9', // lighter'#dd6161': 'ds-1', // is-plain:active'#F56C6C': 'danger', // success'#f67b7b': 'dl-1', // 没有'#f78989': 'dl-2', // hover color'#f89898': 'dl-3', // 没有'#f9a7a7': 'dl-4', // is-disabled hover color'#fab6b6': 'dl-5', // is-disabled color'#fbc4c4': 'dl-6', // border-color'#fcd3d3': 'dl-7', // 没有'#fde2e2': 'dl-8', // light'#fef0f0': 'dl-9', // lighter'#82848a': 'is-1', // is-plain:active'#909399': 'info', // success'#9b9ea3': 'il-1', // 没有'#a6a9ad': 'il-2', // hover color'#b1b3b8': 'il-3', // 没有'#bcbec2': 'il-4', // is-disabled hover color'#c8c9cc': 'il-5', // is-disabled color'#d3d4d6': 'il-6', // border-color'#dedfe0': 'il-7', // 没有'#e9e9eb': 'il-8', // light'#f4f4f5': 'il-9'// lighter};Object.keys(colorMap).forEach((key) => {const value = colorMap[key];data = data.replace(new RegExp(key, 'ig'), value);});return data;}},mounted() {this.initTheme();// 默认从线上官方拉取最新css,2秒钟后做一个检查没有拉到就从本地在拉下// let that = this;// 如果是记住用户的状态就需要,在主题切换的时候记录颜色值,在下次打开的时候从新赋值// this.colors.primary = localStorage.getItem('color')||this.colors.primary//例如// setTimeout(function() {// if (that.originalStyle) {// return;// } else {// that.getIndexStyle(that.cssUrl[1]);// }// }, 2000);// this.$nextTick(() => {// // 获取页面一共引入了多少个style 文件// this.originalStylesheetCount = document.styleSheets.length;// });}
};
</script>
<style lang="scss"></style>
使用
.boxDiv{padding:40px;background: var(--primary-color);box-shadow: 0 0 10px 8px var(--primary-color-light-8);
}