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)