Skip to main content

前景色自动适配技术

一、前景色适配原理

1、CSS 溢出边界渲染特性

当 CSS 属性值超过正常范围时,渲染的值即为合法边界值。

举个例子:

opacity 透明度属性值合法范围是 0-1,如果设置负数或极大值,则浏览器解析为 0 和 1:

.text {
opacity: -1; // 等同于 opacity: 0;
opacity: 2; // 等同于 opacity: 1;
}

什么是 HSL


  • H:即色调(Hue),0/360 红色,120 绿色,240 蓝色。取值为:0 - 360
  • S:即饱和度(Saturation),取值为:0% - 100.0%
  • L:即亮度(Lightness),取值为:0% - 100.0%

同理,HSL 的 L 如果设置负数或极大值,则浏览器解析为 0% 和 100%:

.text {
color: hsl(0, 0%, -999%); // 等同于 hsl(0, 0%, 0%),即黑色
color: hsl(0, 0%, 999%); // 等同于 hsl(0, 0%, 100%),即白色
}

利用这个原理,可以用 HSL 来设置前景(即文本)的颜色。

现在要做的就是,通过底色来判断 HSL 的 L 为负数(-999%)还是极大值(999%)

2、sRGB 相对亮度算法

lightness = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255

通过这个算法,可以根据底色的 rgb 来计算亮度,lightness 结果在 0-1 之间。

  • 底色偏亮:lightness > 0.5
  • 底色偏暗:lightness < 0.5

现在要将 lightness 换算成 HSL 的 L 值,使得底色偏亮时 L 值为负值(黑色),底色偏暗时 L 值为极大值(白色):

// 底色偏亮时(> 0.5),计算结果为负值;底色偏暗时(< 0.5),计算结果为正极大值。
L = (lightness - 0.5) * -999%

到这里,就可以通过底色的亮度来设置前景(文本)颜色了,现在的问题就是要把底色的 rgb 计算传入前景色 hsl。

二、计算并传入前景色

1、通过纯 CSS 传入前景色

  1. :root 定义好底色的 rgb 值;
  2. 通过 var() 代入变量;
  3. 通过 calc() 计算底色亮度,并换算成前景色代入。
:root {
--red: 44;
--green: 135;
--blue: 255;
}

.btn {
background-color: rgb(var(--red), var(--green), var(--blue));
// sRGB 相对亮度计算
--r: calc(var(--red) * 0.2126);
--g: calc(var(--green) * 0.7152);
--b: calc(var(--blue) * 0.0722);
// 底色亮度
--lightness: calc((var(--r) + var(--g) + var(--b)) / 255);
}

.text {
color: hsl(0, 0%, calc((var(--lightness) - 0.5) * -9999%));
}

2、通过 styled-components 传入

使用 styled-components 库,可直接在使用样式组件时传值,在样式组件中进行相应的计算。

import styled from 'styled-components'

export const UITxt = styled.p``
export const UIBtn = styled.div`
${(props) => {
const _red = props.r
const _green = props.g
const _blue = props.b
const _lightness = (_red * 0.2126 + _green * 0.7152 + _blue * 0.0722) / 255
return `
background-color: rgb(${_red}, ${_green}, ${_blue});
${UITxt} {
color: hsl(0, 0%, calc((${_lightness} - 0.5) * -9999%));
}
`
}}
`

const App = () => {
return (
<>
<UIBtn r={44} g={135} b={255}>
<UITxt>Text</UITxt>
</UIBtn>
</>
)
}