前景色自动适配技术
一、前景色适配原理
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 - 360S
:即饱和度(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 传入前景色
- 在
:root
定义好底色的 rgb 值; - 通过
var()
代入变量; - 通过
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>
</>
)
}