需求背景:项目需要支持动态换主题,elementui仅需动态替换主题色
大概的思路就是给html根标签设置一个data-theme属性,然后通过js切换data-theme的属性值,scss根据此属性来判断使用对应主题变量。这里可以选择持久化Vux或接口来保存用户选择的主题。 elementui则参考ThemePicker组件 实现定制主题色
1.主题
1.新建一个Scss文件_themes.scss,里面可以配置不同的主题配色方案
$orange-theme: (
//主题色
main-color: #e6a23c,
//文字颜色
text-color: #999999,
//顶部背景颜色
top-background: #ffffff,
//左菜单栏背景色
sidebar-bg: #ffffff,
);
//蓝色主题
$blue-theme: (
main-color: #409EFF,
text-color: #999999,
top-background: #ffffff,
sidebar-bg: #ffffff,
);
//定义映射集合
$themes: (
orange-theme: $orange-theme,
blue-theme: $blue-theme,
);
2.定义操作,可另外写个scss或者写在一起,上代码:
@mixin themeify {
@each $theme-name, $theme-map in $themes {
$theme-map: $theme-map !global;
[data-theme="#{$theme-name}"] & {
@content;
}
}
}
//从主题色map中取出对应颜色
@function themed($key) {
@return map-get($theme-map, $key);
}
//获取背景颜色
@mixin background_color($color) {
@include themeify {
background-color: themed($color) !important;
}
}
//获取字体颜色
@mixin font_color($color) {
@include themeify {
color: themed($color) !important;
}
}
可根据需求自己往里面加方法
3.具体在vue中使用,直接引入对应混入器就好,取哪个颜色,传哪个key:
<style lang="scss" scoped>
@import '~@/styles/theme/theme.scss';
.main-container{
@include background_color("main-color");
}
</style>
根据自己的路径引入
4.使用js动态切换HTML的属性data-theme的值
关键代码
window.document.documentElement.setAttribute( "data-theme", "blue-theme" );
切换后会发现你的css选择器前面多了一些东西[data-theme="blue-theme"]
2.elementui 主题色切换
这里仅需替换主题色所以采用最简单的方式,参考 vue-element-admin 该项目里面的 ThemePicker组件 可以直接实现该功能,但仔细观察代码后,发现它需要发请求获取到Element UI 的默认样式 index.css,所以需要改一下; 由于项目使用的Element UI的版本是唯一的,直接把 这个样式代码拷贝出来就行了,然后再对其进行操作; index.css 文件路径: node_modules\element-ui\lib\theme-chalk\index.css
1.定义eleUiCss
export default=`此处为拷贝elementui样式`
2。主要实现
/**
* 获取一系列 主题色
* 入参:67c23a
* 结果:['67c23a', '103,194,58', '#76c84e', '#85ce61', '#95d475', '#a4da89', '#b3e19d', '#c2e7b0', '#d1edc4', '#e1f3d8', '#f0f9eb', '#5daf34']
*/
function getThemeCluster(theme, type = '') {
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) {
// when primary color is in its rgb space
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
}
/**
* @param style 老的css样式代码
* @param oldCluster 老的一些列主题色 待替换
* @param newCluster 新的一系列主题色 替换成
*
* @returns newStyle 新的 css样式代码 替换后的
*/
function updateStyle(style, oldCluster, newCluster) {
let newStyle = style
oldCluster.forEach((color, index) => {
// 将老颜色替换成新颜色
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index].trim()) // 全局替换 不区分大小写 去掉多余空格
})
return newStyle
}
import ElementUiCSS from './eleUiCss.js'
export const updateThemeColor = function (val) {
if (typeof val !== 'string' || val.length === 0) return
const ORIGINAL_THEME = `#409EFF`// default color (element ui的默认主题色,所有我们根据这个去改)
const ThemeCode = ElementUiCSS
.replace(/@font-face{[^}]+}/g, '') // 去掉字体样式
.replace(/.el-icon-[a-zA-Z0-9-:^]+before{content:"[^}]+}/g, '') // 去掉图标样式
// 得到一系列 主题色颜色 (我们需要的颜色 '产出')
const themeCluster = getThemeCluster(val.replace('#', ''), 'new')
/**
* 入参:'chalk'(旧css代码), 'chalk-style'(style的id)
* 直接 将老的 css 代码里 待改的旧颜色改成 新颜色 然后将新的样式 插入到head标签里
*/
const getHandler = id => {
return () => {
// 得到一系列 主题色颜色 (原始的一些列颜色 待改)
const originalCluster = getThemeCluster(ORIGINAL_THEME.replace('#', ''))
const newStyle = updateStyle(ThemeCode, 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-style')
chalkHandler()
}
3.调用方法
updateThemeColor(themeColor)
至此完工!