效果展示

less换肤如何实现 less换肤方案_ci

基本原理

目前常见的换肤方案无非几种:

  • 通过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修改颜色。尤其是可以增加颜色选择器进行自定义皮肤的设置。

自定义颜色效果

实现效果

less换肤如何实现 less换肤方案_less_02

实现代码

// 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); // 改变样式
    }
};