前言

经常用Element UI的小伙伴,应该知道,Element UI官方文档,可以自由更换主题。那么,我们怎么把这个功能用到自己项目中呢?其实官方文档也有所说明,这里对其进行更详细,更明确的说明,同时提供简单示例,供大家参考。

Vue + Element UI 如何动态全局主题颜色?

ColorPicker 颜色选择器

ColorPicker 颜色选择器,是Element UI 提供的组件,我们可以直接使用。

<el-color-picker v-model="color"></el-color-picker> ... <script> export default { data() { return { // 默认颜色 color: '#409EFF' } } }; </script>

Vue + Element UI 如何动态全局主题颜色?

使用也是非常简单,下面我们能通过这个组件,改变全局的主题颜色。

Vue + Vuex + Element UI动态全局主题颜色

1、封装一个theme-picker组件

<template> <el-color-picker class="theme-picker" popper-class="theme-picker-dropdown" v-model="theme" :size="size"> </el-color-picker> </template>
import * as types from '../stores/commons/types.js' const version = require('element-ui/package.json').version const ORIGINAL_THEME = '#409EFF' export default { name: 'ThemePicker', props: { // 默认主题,可由外部传入 default: { type: String, default: localStorage.getItem('COLOR_THEME') }, size: { type: String, default: 'small' } }, data () { return { chalk: '', // content of theme-chalk css theme: ORIGINAL_THEME, showSuccess: true // 是否弹出换肤成功消息 } }, mounted () { if (this.default != null) { this.theme = this.default this.$emit('onThemeChange', this.theme) this.$store.commit(types.M_THEME_COLOR, this.theme) this.showSuccess = false } }, watch: { theme (val, oldVal) { if (typeof val !== 'string') return const themeCluster = this.getThemeCluster(val.replace('#', '')) const originalCluster = this.getThemeCluster(oldVal.replace('#', '')) 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 } } const chalkHandler = getHandler('chalk', 'chalk-style') if (!this.chalk) { const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css` this.getCSSString(url, chalkHandler, 'chalk') } else { 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('onThemeChange', val) this.$store.commit(types.M_THEME_COLOR, val) if (this.showSuccess) { this.$message({ message: '换肤成功', type: 'success' }) } else { this.showSuccess = true } } }, 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, callback, variable) { const xhr = new XMLHttpRequest() xhr.onreadystatechange = () => { if (xhr.readyState === 4 && xhr.status === 200) { this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '') callback() } } 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) { 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 } } }
.theme-picker .el-color-picker__trigger { vertical-align: middle; } .theme-picker-dropdown .el-color-dropdown__link-btn { display: none; }

2、解析

监听theme(颜色选择器的值),如果发生变化,通过getThemeCluster函数,计算一系列theme值相关主题颜色。

getCSSString方法,是获取远程(element ui提供)的主题css样式文件。拿回来后,用updateStyle方法,把远程拉下来的样式替换为我们自己重新计算的的颜色。

最后,创建一个style标签,将新的主题样式,写入进去。所有Element UI相关组件主题颜色就会被替换。

但是如果是我们自定义的组件呢?颜色如何处理?

这里我们就用到了vuex + LocalStorage来管理,主题颜色值。

Vuex + LocalStorage动态与Element UI组件无关主题颜色

上面代码种,我们已经将,theme存储了起来,如果有不会vuex + LocalStorage持久化状态管理的小伙伴,可以点击《Vuex+localStorage数据状态持久化》,这里就不做过多的介绍了。

通过commitstore里面存储颜色。

this.$store.commit(types.M_THEME_COLOR, this.theme)

然后通过state获取:

computed: { ...mapState({ themeColor: state => state.commons.themeColor }) },

最后我们页面可以直接使用themeColor

<div :style="{ 'border-color': themeColor, 'background-color': backgroundColor }"></div>

细心的小伙伴是不是发现这里过了一个变量backgroundColor,这个是通过themeColor重新计算的。

backgroundColor () { // 返回某个颜色的三原色 const tintColor = (color) => { let red = parseInt(color.slice(0, 2), 16) let green = parseInt(color.slice(2, 4), 16) let blue = parseInt(color.slice(4, 6), 16) return [red, green, blue] } let color = this.themeColor.replace('#', '') color = tintColor(color) // 转成rgba格式的,并添加透明度 return `rgba(${color[0]}, ${color[1]}, ${color[2]}, .3)` }

这样就搞定了动态主题颜色,是不是很简单了?迫不及待的想让自己的项目也支持动态主题了吧,赶紧自己动手去试试吧。

推荐文章

《Vuex+localStorage数据状态持久化》
《Vuex是什么?Vuex能做什么?Vuex怎么使用?》