前端如何实现更换项目主题色的功能?

1、场景

有一个换主题色的功能,如下图:

切换颜色后,将对页面所有部分的色值进行重新设置,符合最新的主题色。

2、实现思路

因为色值比较灵活,可以任意选取,所以最好的实现方式是,根据设置的色值,拼接相应的 style 标签代码,对样式进行覆盖。

3、实现步骤

3.1、获取深浅色

当前公司的组件库设计的时候,对于同一组件的颜色设置基本有三种:正常色、较深色(常用于hover效果)、较浅色(常用于边框),所以第一步是可以根据正常色,获取深浅色。

3.1.1、获取 HSL 色值

深浅色的获取,基本是通过设置 HSL 颜色的亮度实现,所以第一步是将任意格式的色值,转换为 HSL 格式。

3.1.1.1、将 RGB 颜色转为 HSL

因可能会存入 RGB 颜色,所以需要将 RGB 转为 HSL 。下面是代码:

// rgb to hsl
function rgbToHsl (color) {
  let aColor = color.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')
  const _R = aColor[0] / 255
  const _G = aColor[1] / 255
  const _B = aColor[2] / 255
  const Cmax = Math.max(_R, _G, _B)
  const Cmin = Math.min(_R, _G, _B)
  const V = Cmax - Cmin

  let H = 0
  if (V === 0) {
    H = 0
  } else if (Cmax === _R) {
    H = 60 * (((_G - _B) / V) % 6)
  } else if (Cmax === _G) {
    H = 60 * ((_B - _R) / V + 2)
  } else if (Cmax === _B) {
    H = 60 * ((_R - _G) / V + 4)
  }

  H = Math.floor(backCycle(H, 360))
  const L = numberFixed((Cmax + Cmin) / 2)
  const S = V === 0 ? 0 : numberFixed(V / (1 - Math.abs(2 * L - 1)))
  return `hsl(${H},${numberFixed(100 * S)}%,${numberFixed(100 * L)}%)`
}


function numberFixed (num = 0, count = 3) {
  const power = Math.pow(10, count)
  return Math.floor(num * power) / power
}


function backCycle (num, cycle) {
  let index = num % cycle
  if (index < 0) {
    index += cycle
  }
  return index
}
3.1.1.2、将 HEX 颜色转为 HSL

因可能会存入 HEX 颜色,所以需要将 HEX 转为 HSL 。具体步骤是,先将 HEX 转为 RGB,再将 RGB 转为 HSL

// hex 转 rgb
function hexToRgb (color) {
  if (color.length === 4) {
    let sColorNew = '#'
    for (let i = 1; i < 4; i += 1) {
      sColorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1))
    }
    color = sColorNew
  }
  // 处理六位的颜色值
  let sColorChange = []
  for (let i = 1; i < 7; i += 2) {
    sColorChange.push(parseInt('0x' + color.slice(i, i + 2)))
  }
  return 'RGB(' + sColorChange.join(',') + ')'
}


RGB 转 HSL 见上部分。
3.1.2、HSL色值调亮度

HSL 调亮度,主要是改最后一位数字的值,代码如下:

// 获取更浅或者更暗的颜色,color为主色;amt为正数时获得浅色,为负数时获得深色。
function lightenDarkenColor (color, amt) {
  let hColor = colorToHsl(color).replace(/(?:\(|\)|hsl|HSL)*/g, '').split(',')
  let H = hColor[0]
  let S = getPercentNumber(hColor[1])
  let L = getPercentNumber(hColor[2]) + amt / 100
  L = L > 1 ? 0.98 : L
  return `hsl(${H},${numberFixed(100 * S)}%,${numberFixed(100 * L)}%)`
}


// 获取百分数的数值
function getPercentNumber (numberStr) {
  numberStr += ''
  if (numberStr.indexOf('%') > -1) {
    return parseFloat(numberStr) / 100
  } else {
    return parseFloat(numberStr)
  }
}

3.2、生成 style 代码

根据上面生成的深浅色,拼接 style 代码,对组件库和项目的色值进行覆盖,需要注意组件库的代码和项目中的代码要分开。

代码生成示例如下:

// 顶部导航的换肤 class
export let headerSkin = ({ skinColor, skinColorDarken, skinColorLighten } = {}) => {
  return `
        #skin .a-n-menu-primary ,
        #skin .a-n-menu-submenu .a-n-menu-submenu .a-n-menu .a-n-menu-item-active:not(.a-n-menu-submenu):before{
            background: ${skinColor};
        }

        #skin .a-n-menu-horizontal .a-n-menu-submenu:hover,
        #skin .a-n-menu-horizontal .a-n-menu-submenu:active,
        #skin .a-n-menu-horizontal .a-n-menu-item:hover,
        #skin .a-n-menu-horizontal .a-n-menu-item-active {
            background: ${skinColorDarken};
        }

        #skin .a-n-menu-vertical .a-n-menu-submenu .a-n-menu-item-selected>span.slotItem{
            color: ${skinColor};
        }

        #skin .a-n-menu-submenu {
            .a-select-dropdown {
                .a-n-menu-item:hover{
                    background: ${skinColorLighten}
                }
            }
        }
        #skin .aw-header .icon-container-right:hover,
        #skin .aw-header .icon-container:hover {
            background: ${skinColorDarken};
        }

    `
}

3.3、将 style 放到 head 中,完成主题色的替换

let css = 上面生成的style代码。
var head = document.getElementsByTagName('head')[0]
skinStyle = document.createElement('style')
skinStyle.innerHTML = css
head.appendChild(skinStyle)
相关推荐
我要洋人死27 分钟前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人38 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人39 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR44 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596931 小时前
前端预览word、excel、ppt
前端·word·excel
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風6 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#