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

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)
相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端
爱敲代码的小鱼9 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax