效果展示
基本原理
目前常见的换肤方案无非几种:
- 通过class选择器/id选择器选中需要更换的DOM,直接修改颜色
- 书写多套css,通过js进行css的切换
- 使用modifyVars()方法,修改less.js中相应的less变量更换颜色
在这里提供一种新知晓的换肤开发方案方案分享一下:
使用less,将其中的颜色提升到全局,各自组件/页面引用‘顶级’颜色,通过js修改顶级颜色达到各模块换肤的目的。
开发过程
安装及配置
npm install style-resources-loader -D
npm install vue-cli-plugin-style-resources-loader -D
npm install less-loader@5.0.0 --save
npm install less --save
安装成功后需要在 vue.config.js 文件中进行配置
const path = require("path");
module.exports = {
lintOnSave: false,
pluginOptions: {
"style-resources-loader": {
preProcessor: "less",
patterns: [
// 此处是核心样式变量
path.resolve(__dirname, "./src/theme/style.less"),
],
},
},
}
开发过程
在 src 目录下创建 theme 文件夹,其中创建 style.less 文件。
// 默认的主题颜色
@primaryColor: var(--primaryColor, #000);
@primaryTextColor: var(--primaryTextColor, green);
// 导出变量
:export {
name: "less";
primaryColor: @primaryColor;
primaryTextColor: @primaryTextColor;
}
此刻就已经将 style.less 文件中的变量提升到了全局中,在项目中任意组件/页面处都可以使用其中的变量。
<template>
<div>
<h1 class="theme-h1">这是一段测试文字</h1>
</div>
</template>
<style lang="less" scoped>
.theme-h1 {
color: @primaryTextColor;
}
</style>
编写可以更换的主题颜色(推荐使用rgba颜色,方便控制透明度):
// ./src/theme/modal.js
export const themes = {
default: {
primaryColor: `rgba(74,144,226,0.6)`,
primaryTextColor: `rgba(74,144,226,1)`,
},
dark: {
primaryColor: `rgba(0,0,0,0.4)`,
primaryTextColor: `rgba(0,0,0,1)`,
},
};
编写切换主题方法(此处可以增加接口获取当前主题,或者使用 localStorage 在前端存储主题)
import { themes } from "@/theme/modal.js";
// 修改页面中的样式变量值
const changeTheme = (obj) => {
for (let key in obj) {
document.getElementsByTagName("body")[0].style.setProperty(`--${key}`, obj[key]);
}
};
export const setTheme = (themeName) => {
const themeData = themes[themeName];
changeTheme(themeData);
};
后续建议
- 推荐将全部的可变区域颜色都抽取出来,放在 style.less 文件中,方便后续管理
- 推荐前端切换皮肤时,将当前皮肤保存在数据库或者localStorage中
- 推荐将 modal.js 中的颜色色值变为可修改的模板字符串形式,或者以‘+’连接的字符串形式,方便通过外部js修改颜色。尤其是可以增加颜色选择器进行自定义皮肤的设置。
自定义颜色效果
实现效果
实现代码
// index.vue
<template>
<div id="app">
<h1 class="theme-h1">这是一段测试文字</h1>
<div class="opera-btn">
<input type="color" @input="changeColor">
</div>
</div>
</template>
<script>
import { setTheme } from "./theme/theme";
export default {
name: 'index',
methods: {
changeColor({target: {value}}) {
let color = this.getOpacityColor(value, 1, 'string');
setTheme('custom', {
primaryColor: color
});
},
/**
* 16进制转rgba
* @param {String} thisColor 16进制色值
* @param {Number} thisOpacity 透明度 默认值 1
* @param {String} state 返回值状态 string / array 默认值 string
* */
getOpacityColor(thisColor, thisOpacity=1, state='string') {
let theColor = thisColor.toLowerCase();
//十六进制颜色值的正则表达式
let r = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
// 如果是16进制颜色
if (theColor && r.test(theColor)) {
if (theColor.length === 4) {
let sColorNew = "#";
for (let i = 1; i < 4; i += 1) {
sColorNew += theColor.slice(i, i + 1).concat(theColor.slice(i, i + 1));
}
theColor = sColorNew;
}
//处理六位的颜色值
let sColorChange = [];
for (let i = 1; i < 7; i += 2) {
sColorChange.push(parseInt("0x" + theColor.slice(i, i + 2)));
}
// 返回值状态
if(state === 'string') {
return "rgba(" + sColorChange.join(",") + "," + thisOpacity + ")";
}else if(state === 'array') {
return sColorChange;
}
}
return theColor;
}
}
}
</script>
<style lang="less" scoped>
.theme-h1 {
color: @primaryColor;
}
</style>
// theme.js
import { themes } from "./modal.js";
// 修改页面中的样式变量值
const changeStyle = (obj) => {
for (let key in obj) {
document
.getElementsByTagName("body")[0]
.style.setProperty(`--${key}`, obj[key]);
}
};
export const setTheme = (themeName, themeData) => {
const themeConfig = themes[themeName];
// 如果有主题名称,那么则采用我们定义的主题
if (themeName === 'custom') {
changeStyle(themeData);
} else if (themeConfig) {
changeStyle(themeConfig); // 改变样式
}
};