关于javascript:工作问题如何给已有项目增加主题换色功能

56次阅读

共计 5667 个字符,预计需要花费 15 分钟才能阅读完成。

一. 前言

  之前我的项目须要为其减少主题换色性能,接手这个需要后,就开始剖析我的项目的代码内容。我把他们次要分成两个大类,一是 elementUI 下的组件,二是自定义模板或者组件。这两类都须要对其款式进行切换来达到换色成果。为了做到尽可能的优雅,最好是只扭转一处中央,也不必反复写一样的款式,就能够实现换色。我这种菜鸡真是花了不少工夫在下面。

二. 方案设计

计划 1.link 标签动静引入或应用不同类名切换不同主题

  设计几套不同主题款式的文件,在须要的时候,创立 link 标签动静加载到 head 标签中,或者是动静扭转 link 标签的 href 属性。或者在须要切换主题的时候根标签切换对应的类名来切换不同的款式。
  然而这样子的操作的话,如果须要更改某一样式,所有主题都须要更改,麻烦且容易出错,这能够说是最笨的办法。

计划 2. 应用 css 变量 + 类名切换

  这是计划 1 的改良计划。大体思路跟计划 1 类似,仍然是提前将款式文件载入,切换时将指定的根元素类名更换。不过这里绝对灵便的是,默认在根作用域下定义好 CSS 变量,只须要在不同的主题下更改 CSS 变量对应的取值即可。
  这样子操作的益处就是通过 CSS 变量,能够达到只改一次就能全副更改的成果。

计划 3. 应用 CSS 变量 + 动静 setProperty

  计划 3 较于前两种会更加灵便,不过视状况而定,这个计划实用于由用户依据色彩面板自行设定各种色彩主题,这种是主题色彩不确定的状况,而前两种计划更实用于定义预设的几种主题。

三. 计划实操

  综合思考之后,决定应用第三种计划,因为第三种计划足够灵便,能够通过 js 设置几个默认主题,也能够交给用户去取色来设置新主题。这样再当前需要有变的时候批改起来更有劣势。

以下内容将以计划 3 来开展:
在我的项目内新建 theme 文件夹,搁置如下文件内容:

  如上,index.css 与 fonts 内的字体文件,是预设好的 elementUI 款式模板,依据用户的取色或者预设的色彩来替换文件内对应色彩,再全局搁置到 html 的 style 标签内,即可实现 elementUI 的主题色彩替换。而自定义模板或者组件的色彩款式则是由另外的文件来实现。

首先先看看 elementUI 的主题如何替换:

// 装置 css-color-function,前面会用来转换色彩是 rgb 还是 16 进制
npm css-color-function // yarn add css-color-function

通过 formula.json 获取色彩变动规定,再用 generateColors 函数转化色彩

// formula.json
{"primary": "color(primary)",
  "shade-1": "color(primary shade(10%))",
  "shade-2": "color(primary shade(20%))",
  "shade-3": "color(primary shade(30%))",
  "shade-4": "color(primary shade(40%))",
  "shade-5": "color(primary shade(50%))",
  "shade-6": "color(primary shade(60%))",
  "shade-7": "color(primary shade(70%))",
  "shade-8": "color(primary shade(80%))",
  "shade-9": "color(primary shade(90%))",
  "alpha-1": "color(primary alpha(.1))",
  "alpha-2": "color(primary alpha(.2))",
  "alpha-3": "color(primary alpha(.3))",
  "alpha-4": "color(primary alpha(.4))",
  "alpha-5": "color(primary alpha(.5))",
  "alpha-6": "color(primary alpha(.6))",
  "alpha-7": "color(primary alpha(.7))",
  "alpha-8": "color(primary alpha(.8))",
  "alpha-9": "color(primary alpha(.9))",
  "light-1": "color(primary tint(10%))",
  "light-2": "color(primary tint(20%))",
  "light-3": "color(primary tint(30%))",
  "light-4": "color(primary tint(40%))",
  "light-5": "color(primary tint(50%))",
  "light-6": "color(primary tint(60%))",
  "light-7": "color(primary tint(70%))",
  "light-8": "color(primary tint(80%))",
  "light-9": "color(primary tint(90%))"
}
// color.js  
import color from 'css-color-function'
import formula from './formula.json'
 
export function generateColors(primary) {let colors = {}
 
  Object.keys(formula).forEach((key) => {const value = formula[key].replace(/primary/g, primary)
    const c = color.convert(value)
    colors[key] = c.indexOf('rgba') > -1 ? c : colorRgbToHex(c)
  })
 
  return colors
}
 
/* 将 rgb 色彩转成 hex */
export function colorRgbToHex(rgb) {let [r, g, b] = rgb.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')
 
  return '#' + ((1 << 24) + (Number(r) << 16) + (Number(g) << 8) + Number(b)).toString(16).slice(1)
}

预设了各个主题的默认色彩供调用。若有多种主题,间接写即可。后续若主题较多,可应用混入性能。

//model.js

let themes = {
    default: {
        color_block: "#07b6b5",
        color_line: "#009aa6"
    }
}

export {themes}

elementUI 主题色替换函数代码:

//index.js

import {generateColors} from './color'
let style = require('!!css-loader?sourceMap=false!postcss-loader?{"postcssOptions":{"plugins":{"cssnano":{}}}}!./index.css').toString();
import formula from './formula.json'

let originalStyle = ''

export function writeNewStyle(themeColor) {let colors = generateColors(themeColor)
  let cssText = originalStyle
  let colorsCssText = ''
  Object.keys(colors).forEach((key) => {cssText = cssText.replace(new RegExp('(:|\\s+)' + key, 'g'), '$1' + colors[key])
    colorsCssText += `
      .color-${key}{color: ${colors[key]}!important;}
      .bg-${key}{background-color: ${colors[key]}!important;}
      .border-${key}{border-color: ${colors[key]}!important;}
    `
  })
  console.log("cssText", cssText)
  let styleTag = document.getElementById('ide-theme-style')
  if (!styleTag) {styleTag = document.createElement('style')
    styleTag.setAttribute('id', 'ide-theme-style')
    document.head.appendChild(styleTag)
  }
  styleTag.innerText = cssText + colorsCssText
}

export function getIndexStyle(themeColor) {if (!originalStyle) {originalStyle = getStyleTemplate(style, themeColor)
    }
    writeNewStyle(themeColor)  
}

export function getStyleTemplate(data, themeColor) {let colors = generateColors(themeColor)
  const colorMap = new Map()
  Object.keys(formula).forEach((key) => {colorMap.set(colors[key], key)
  })

  for (let [key, value] of colorMap) {data = data.replace(new RegExp(key, 'ig'), value)
  }

  return data
}

自定义模板或者组件替换主题色函数代码:

//theme.js

import {themes} from "./model";
import {getIndexStyle} from "./index.js"

const changeStyle = (obj) => {for (let key in obj) {
        document
          .getElementsByTagName("body")[0]
          .style.setProperty(`--${key}`, obj[key]);
    }
};

// 设置主题的色彩
const setThemeColor = (theme) => {localStorage.setItem("theme", JSON.stringify(theme)); // 保留主题到本地,下次进入应用该主题
    changeStyle(theme); // 扭转款式
}
const setFontFamily = (FontFamilyName) => {localStorage.setItem("FontFamily", FontFamilyName);
    const themeConfig = {themeFontFamily: FontFamilyName};
    changeStyle(themeConfig);
}

export const setTheme = (type = 'themeColor') => {if (type === 'themeColor') {let currentTheme = localStorage.getItem("theme") && JSON.parse(localStorage.getItem("theme"));
        if (!currentTheme) {currentTheme = themes.default}
        setThemeColor(currentTheme);
        getIndexStyle(currentTheme.color_block)
    } else if (type === 'FontFamily') {let FontFamily = localStorage.getItem("FontFamily");
        setFontFamily(FontFamily);
    }
};

以上为 theme 文件夹文件介绍。调用的入口函数是 theme.js 文件里的 setTheme 函数。
须要在我的项目工程的 main.js 文件内引入 setTheme 函数。
如:

import {setTheme} from "@/theme/theme";
// 而后在 created 生命周期里调用函数
// 初始化主题色
setTheme && setTheme();

注:须要留神的是,index.css 文件的调用能够有两种模式,一种是从后盾获取,一种是从本地调用。后盾获取比拟好了解,然而前端获取 CSS 文件的纯文本内容就要折腾一下了。过后也花了些工夫去解决。当初给出一个本人的解决方案以供参考:

应用 require 形式获取文本再转为 string:

//css 引入
let style = require('!!css-loader?sourceMap=false!postcss-loader?{"postcssOptions":{"plugins":{"cssnano":{}}}}!./index.css').toString();

//scss 引入
let style = require('!!css-loader?sourceMap=false!postcss-loader?{"postcssOptions":{"plugins":{"cssnano":{}}}}!sass-loader!./style.scss').default.toString();

//less 引入
let style = require('!!css-loader?sourceMap=false!postcss-loader?{"postcssOptions":{"plugins":{"cssnano":{}}}}!less-loader!./style.less').default.toString();

正文完
 0