换肤方案输出

693 阅读5分钟

需求:

1、静态换肤根据需求生成几套皮肤方案,进行静态的样式切换,可选范围不大
2、动态换肤根据用户选择或者后端返回值,动态进行换肤,可选范围较大

目标

纯色:背景色、文字颜色、纯色小图标(转为svg或者font通过color控制色值)通过css变量处理
1.背景色渐变(通过css变量处理)
2.文字渐变(存在兼容性问题)[解决方案](https://www.jianshu.com/p/e47837d80283)第三种方式
3.小图标渐变(转为font实现渐变依赖文字渐变的兼容性问题处理是否恰当)
4.小图标渐变(利用svg图片,直接使用svg(而不是将svg静态文件路径导入并赋值给img-src的方式)通过动态修改svg的fill来实现颜色改变)
// svg文件
<svg class="remind" width="27px" height="27px" viewBox="0 0 27 27" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <defs>
        <linearGradient x1="-12.9830651%" y1="21.015625%" x2="92.220127%" y2="100%" id="linearGradient-1">
            <stop stop-color="#0043FF" offset="0%"></stop>
            <stop stop-color="#A370F1" offset="100%"></stop>
        </linearGradient>
        <linearGradient x1="-12.9830651%" y1="21.015625%" x2="92.220127%" y2="100%" id="linearGradient-2">
            <stop stop-color="#000" offset="0%"></stop>
            <stop stop-color="#ccc" offset="100%"></stop>
        </linearGradient>
    </defs>
    <path d="M19.937766,15.9613525 C19.581262,15.783226 19.358447,15.560568 19.180195,15.204315 C18.868254,14.5363408 18.912817,13.6902401 18.95738,12.7550761 C19.001943,12.0871019 19.046506,11.4191276 18.95738,10.795685 C18.779128,9.54879973 18.288935,8.48004094 17.62049,7.58940861 C16.6401041,6.38705496 15.2140881,5.58548587 13.6989461,5.40735941 L13.6989461,4.96204323 C13.6989461,4.60579029 13.3870052,4.29406898 13.0305012,4.29406898 C12.6739972,4.29406898 12.3620562,4.60579029 12.3620562,4.96204323 L12.3620562,5.45189102 C11.8718632,5.49642263 11.3816702,5.63001748 10.8914772,5.80814396 C9.19808326,6.52064981 7.81663031,7.99019315 7.28187432,9.86052104 C7.01449633,10.795685 7.05905932,11.6863173 7.10362233,12.6214813 C7.14818532,13.6457085 7.23731133,14.625404 6.79168133,15.3824415 C6.65799233,15.6496312 6.47974034,15.7386944 6.03411036,15.9168209 C5.41022837,16.2285422 5.00916138,16.8519848 5.00916138,17.5644907 C5.05372437,18.455123 5.76673237,19.2121605 6.70255534,19.2121605 L10.1339062,19.2121605 C10.3567212,20.6371722 11.5599222,21.705931 13.0305012,21.705931 C14.5010801,21.705931 15.7488441,20.5926406 15.9270961,19.2121605 L19.358447,19.2121605 C20.29427,19.2121605 21.0072779,18.455123 21.007278,17.5199591 C21.051841,16.5402635 20.29427,16.139479 19.937766,15.9613525 Z M13.0305012,20.4145141 C12.2729302,20.4145141 11.6936112,19.9246663 11.4707962,19.2566921 L14.5456431,19.2566921 C14.3673911,19.9246663 13.7880721,20.4145141 13.0305012,20.4145141 Z M19.358447,17.876212 L6.70255534,17.876212 C6.52430335,17.876212 6.39061436,17.6980855 6.34605135,17.5199591 C6.34605135,17.3418326 6.43517735,17.2082378 6.56886635,17.1637061 C7.01449633,16.9855797 7.54925231,16.71839 7.90575631,16.0504157 C8.57420129,14.9371253 8.48507529,13.69024 8.39594928,12.532418 C8.35138629,11.7308489 8.30682328,10.9292798 8.48507529,10.2613056 C8.93070527,8.79176225 10.0002172,7.63394022 11.3371072,7.09956082 C11.8273002,6.87690273 12.3620562,6.78783951 12.8968122,6.78783951 L12.9413752,6.78783951 C14.2782651,6.78783951 15.6151551,7.41128215 16.5064151,8.52457255 C17.0411711,9.1925468 17.442238,10.0831791 17.575927,11.0628747 C17.665053,11.5527225 17.62049,12.1316335 17.575927,12.7550761 C17.5313641,13.7793033 17.442238,14.8480621 17.932431,15.8277576 C18.199809,16.4066687 18.645439,16.8965164 19.269321,17.1637061 C19.625825,17.3418326 19.625825,17.3863642 19.625825,17.5199591 C19.714951,17.7426171 19.581262,17.876212 19.358447,17.876212 L19.358447,17.876212 Z" id="形状"></path>
</svg>
// css文件结合全局的css变量处理实现动态切换
.remind {
    fill: var(--remind-icon-url);
}


方案优点缺点
class命名空间简单、容易理解、容易实现需要定义class、手动维护、容易混乱
生成多套CSS皮肤:利用CSS预处理语言(如:Less,stylus 或 sass)以及 Webpack、gulp等工具输出多套主题样式。页面加载后,根据用户需求通过js动态的link对应的皮肤样式。好理解需要手写两份以上CSS配色样式;切换样式需要下载CSS的时间
CSS变量换肤:利用CSS变量设置颜色, 用js动态修改CSS变量,进而换色。如果不考虑兼容性,这是最佳换肤方案只需一套CSS文件;换肤不需要延迟等候;对浏览器性能要求低;可自动适配多种主题色不支持IE
Less 在线编译:使用 modifyVars()方法, 基于 less 在浏览器中的编译来实现。在引入less文件的时候需要通过link方式引入,然后基于less.js中的方法来进行修改less变量编译速度依赖客户端性能、切换不及时运行时编译、需要额外引入less.main.js、样式文件通过link方式引入
sass变量:Sass变量(variable),嵌套(nestend rules),混合(mixins),Sass Maps的函数map-get(map,map,key)拓展性较强需要定义较多mixin
elementui实现:变量替换通过定义函数的形式自动替换、操作性较强需要有统一打包出来的index.css

1、class命名空间

// default.theme.css
.button-reset{
    background: linear-gradient(180deg, red, purple);
}
.search-query{
    color: black!important;
    background: linear-gradient(180deg, green, purple)!important;
}
// dark.theme.css
.custom-theme{
    .button-reset{
        background: linear-gradient(180deg, red, purple);
    }
    .search-query{
        color: black!important;
        background: linear-gradient(180deg, green, purple)!important;
    }
}

// 通过方法动态的给body添加class

/**
 * @param {HTMLElement} element
 * @param {string} className
 */
function toggleClass(element:any, className:any) {
    if (!element || !className) {
      return
    }
    let classString = element.className
    const nameIndex = classString.indexOf(className)
    if (nameIndex === -1) {
      classString += '' + className
    } else {
      classString =
        classString.substr(0, nameIndex) +
        classString.substr(nameIndex + className.length)
    }
    element.className = classString
}

2、利用webpack打包出多套css根据用户需求动态的通过js LINK 对应的皮肤

3、通过css变量兼容性方案,默认的css变量方法不兼容IE

// 1、定义variable.js
// 字体变量
const baseSize = {  "--font-size-large-x""22px",  "--font-size-large""18px",  "--font-size-medium""14px",  "--font-size-medium-x""16px",  "--font-size-small-s""10px",  "--font-size-small""12px",};
//浅色
export const lightTheme = {"--background-linear": "linear-gradient(180deg, rgba(0, 67, 255, 1), rgba(163, 112, 241, 1))",  "--fill-1""#fff",  "--text""#3c3c3c",  "--text-1""#757575",  "--text-2""#222",  ...baseSize,};
// 深色
export const darkTheme = { "--background-linear": "linear-gradient(180deg, red, green)""--fill-1""#222",  "--text""#fff",  "--text-1""rgba(255, 255, 255, 0.3)",  "--text-2""#ffcd32",  ...baseSize,};

//2、定义theme.js 需要安装css-vars-ponyfill 插件
在传统浏览器和现代浏览器中为CSS自定义属性(又名“CSS变量”)提供客户端支持的ponyfill。

import { lightTheme, darkTheme } from "../src/assets/js/variable";
import cssVars from "css-vars-ponyfill";
export const initTheme = (theme) => {
  document.documentElement.setAttribute("data-theme", theme ? "light" : "dark");
  cssVars({
    watch: true// 当添加,删除或修改其<link>或<style>元素的禁用或href属性时,ponyfill将自行调用
    variables: theme ? lightTheme : darkTheme, // variables 自定义属性名/值对的集合
    onlyLegacy: false// false  默认将css变量编译为浏览器识别的css样式  true 当浏览器不支持css变量的时候将css变量编译为识别的css
  });
};
//3、使用
.text {
  display: inline-block;
  vertical-align: top;
  line-height: 70px;
  font-size: var(--font-size-large);
  color: var(--text-2);
  background: var(--background-linear);
}
// 4、切换
    const onChange = (checked: boolean) => {
        // toggleClass(document.body, 'custom-theme')
        setTheme(!theme)
        console.log(theme)
        initTheme(theme)
        if (checked) {
            // document.documentElement.setAttribute('data-theme', 'dark')
        } else {
            // document.documentElement.setAttribute('data-theme', 'light')
        }
    }


4、LESS变量运行时编译

1、less.js中的方法来进行修改less变量
less.modifyVars({ '@themeColor': 'blue' });
2、link方式引入less文件
<link rel="stylesheet/less" type="text/css" href="./src/less/public.less" />

5、SASS变量的方式

1variable.scss定义变量
$colors-light: ("text": #fff, "normal": 18px, "large": 24px, "bgcolor": red);
$colors-dark: ("text": #3c3c3c, "normal": 18px, "large": 24px, "bgcolor": green);
2、定义mixin.scss
// mixin.scss// 背景色
@mixin bg-color($key) {
    background: map-get($map: $colors-light, $key: $key)!important;
    [data-theme="dark"] & {
        background: map-get($map: $colors-dark, $key: $key)!important;
    }
}

// text色
@mixin text-color($key) {
    color: map-get($map: $colors-light, $key: $key)!important;
    [data-theme="dark"] & {
        color: map-get($map: $colors-dark, $key: $key)!important;
    }
}
3、使用
.button-reset{
  @include text-color(text);
  @include bg-color(bgcolor);
}

6、变量替换的方式

1、获取index.css
getIndexStyle () {
    this.getFile('//unpkg.com/element-ui/lib/theme-chalk/index.css').then(({data }) => {
    this.originalStyle = this.getStyleTemplate(data)
  })
},
2、将对应需要替换的颜色替换为变量
getStyleTemplate (data) {
    const colorMap = {
      '#3a8ee6': 'shade-1',
      '#409eff': 'primary',
      '#53a8ff': 'light-1',
      '#66b1ff': 'light-2',
      '#79bbff': 'light-3',
      '#8cc5ff': 'light-4',
      '#a0cfff': 'light-5',
      '#b3d8ff': 'light-6',
      '#c6e2ff': 'light-7',
      '#d9ecff': 'light-8',
      '#ecf5ff': 'light-9'
    }
    Object.keys(colorMap).forEach(key => {
      const value = colorMap[key]
      data = data.replace(new RegExp(key, 'ig'), value)
    })
    return data
  },
3、根据自定义的主题变量json数据将变量替换回对应色值
皮肤主题json
{
  "shade-1": "color(primary shade(10%))",
  "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%))"
}

let cssText = this.originalStyle
    Object.keys(this.colors).forEach(key => {
      cssText = cssText.replace(new RegExp('(:|\\s+)' + key, 'g'), '$1' + this.colors[key])
})
4、挂载

if (this.originalStylesheetCount === document.styleSheets.length) {
  const style = document.createElement('style')
  style.innerText = cssText
  document.head.appendChild(style)
} else {
  document.head.lastChild.innerText = cssText
}

实现细节参考:element