1、场景

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

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

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)

JHCan333
10 声望2 粉丝