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] / 255const _G = aColor[1] / 255const _B = aColor[2] / 255const Cmax = Math.max(_R, _G, _B)const Cmin = Math.min(_R, _G, _B)const V = Cmax - Cminlet H = 0if (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 % cycleif (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 / 100L = L > 1 ? 0.98 : Lreturn `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)