byzhangxinxu from https://www.zhangxinxu.com/wordpress/?p=8169
一, 颜色匹配效果预览
如下 GIF 示意, 当我们按钮背景色逐渐变淡的时候, 文字颜色也从原来的白色变成黑色了, 同时边框也显示出来了, 其中的配色转换是纯 CSS 实现的.
您可以狠狠地点击这里: 按钮文字及边框颜色随着背景色自动变化 demo
拖动 R,G,B 对应滑块, 可以看到按钮文字颜色以及边框颜色, 会自动根据背景色不同而发生变化. 具体表现为:
深色背景下, 文字白色, 无边框.
浅色背景下, 文字黑色, 有加深边框, 便于和周围环境区分开.
这种智能匹配是纯 CSS 实现的, 主要是使用 CSS3 calc() 计算, 以及 CSS3 原生 var 变量.
二, 配色代码展示
html 代码很简单, 如下:
<button class="btn"> 我是按钮 </button>
重点和难点在 CSS 部分:
- :root {
- /* 定义 RGB 变量 */
- --red: 44;
- --green: 135;
- --blue: 255;
- /* 文字颜色变色的临界值, 建议 0.5~0.6 */
- --threshold: 0.5;
- /* 深色边框出现的临界值, 范围 0~1, 推荐 0.8+*/
- --border-threshold: 0.8;
- }
- .btn {
- /* 按钮背景色就是基本背景色 */
- background: rgb(var(--red), var(--green), var(--blue));
- /**
- * 使用 sRGB Luma 方法计算灰度 (可以看成亮度)
- * 算法为:
- * lightness = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255
- */
- --r: calc(var(--red) * 0.2126);
- --g: calc(var(--green) * 0.7152);
- --b: calc(var(--blue) * 0.0722);
- --sum: calc(var(--r) + var(--g) + var(--b));
- --lightness: calc(var(--sum) / 255);
- /* 设置颜色 */
- color: hsl(0, 0%, calc((var(--lightness) - var(--threshold)) * -999999%));
- /* 确定边框透明度 */
- --border-alpha: calc((var(--lightness) - var(--border-threshold)) * 100);
- /* 设置边框相关样式 */
- border: .2em solid;
- border-color: rgba(calc(var(--red) - 50), calc(var(--green) - 50), calc(var(--blue) - 50), var(--border-alpha));
- }
乍一看, 犹如鸭子听雷 -- 不知所云, 其实不复杂, 且容我剖析下实现原理.
三, 前景背景自动配色原理
1. CSS 属性值范围溢出边界渲染特性
CSS 这门语言有个很有意思的特性, 就是 CSS 属性值超过正常的范围的时候, 只要格式正确, 也会渲染, 而渲染的值就是合法边界值.
举两个板栗:
opacity 透明度属性值合法范围是 0-1, 但是, 你设置负数, 或者极大值, 浏览器也能解析, 只是要么是 0, 要么是 1 而已, 如下:
- .example {
- opacity: -2; /* 解析为 0, 完全透明 */
- opacity: -1; /* 解析为 0, 完全透明 */
- opacity: 2; /* 解析为 1, 完全不透明 */
- opacity: 100; /* 解析为 1, 完全不透明 */
- }
色值, 如 HSL,S 和 L 的范围都是 0%-100%, 但是, 你设置负数, 或者极大值, 浏览器也能解析, 只是要么是 0%, 要么是 100% 而已, 如下:
- .example {
- color: hsl(0, 0%, -100%); /* 解析为 hsl(0, 0%, 0%), 黑色 */
- color: hsl(0, 0%, 200%); /* 解析为 hsl(0, 0%, 100%), 白色 */
- }
本文的配色技术就活用了这种边界渲染特性.
2. var 变量传递给 calc 实现复杂计算
我们对 CSS 代码从上往下逐个剖析下.
首先, 在: root 根选择器上定义几个全局 CSS 变量 (语义上的全局):
- :root {
- --red: 44;
- --green: 135;
- --blue: 255;
- --threshold: 0.5;
- --border-threshold: 0.8;
- }
其中:
-threshold
这个是 color 变色的临界值, 用来和当前 RGB 颜色值的亮度对比.
-border-threshold
这个是边框颜色透明度的临界值, 同样也是和当前 RGB 颜色值的亮度对比.
然后是 .btn{} 内部的 CSS 代码:
background: rgb(var(--red), var(--green), var(--blue));
这个很好理解, 就是基本的 RGB 色值作为背景色.
- --r: calc(var(--red) * 0.2126);
- --g: calc(var(--green) * 0.7152);
- --b: calc(var(--blue) * 0.0722);
- --sum: calc(var(--r) + var(--g) + var(--b));
- --lightness: calc(var(--sum) / 255);
这里 5 行 5 个 CSS 变量, 需要的其实是最后一个 --lightness , 就是计算当前背景色的亮度. 用的是使用 sRGB Luma 灰度算法 1 , 为什么需要 5 行呢? 因为计算公式就是如此:
lightness = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255
其中, R,G,B 色值相乘的系数就是固定的, 不同灰度算法系数还不一样. --lightness 表示亮度, 范围是 0-1, 此时就可以和 --threshold 和 --border-threshold 这两个临界值比对, 来确定按钮的文字颜色, 边框透明度.
1 这里的灰度可以看成是亮度, 实际上 HSL 的亮度计算方法应该是, R,G,B 中的色值最大值和最小值之和的二分之一.
color: hsl(0, 0%, calc((var(--lightness) - var(--threshold)) * -999999%))
设置颜色的 CSS 代码.
此处 calc 计算翻译成中文就是:(亮度值 - 临界值) * 比例系数.
其中亮度值在 0-1 之间游走, 临界值是固定的 0.5, 于是:
如果亮度值小于 0.5, 亮度值减去临界值为负, 由于我们的比例系数是很大很大的负数, 负负得正, 于是, 会得到一个巨大的正数, 按照边界渲染原理, 会按照 100% 渲染, 于是颜色是白色;
如果亮度值大于 0.5, 亮度值减去临界值为正, 由于我们的比例系数是很大很大的负数, 于是, 会得到一个巨大的负数, 按照边界渲染原理, 会按照 0% 渲染, 于是颜色是黑色;
以上就是按钮文字颜色变色背景下黑色, 深色背景下白色的原理.
--border-alpha: calc((var(--lightness) - var(--border-threshold)) * 100);
边框透明度是类似的原理. 此处 calc 计算翻译成中文就是:(亮度值 - 边框临界值) * 100.
其中亮度值在 0-1 之间游走, 边框临界值是固定的 0.8, 于是:
如果亮度值小于 0.8, 亮度值减去边框临界值为负, 在 CSS 中, 负数透明度会按照边界 0 渲染, 此时边框完全透明;
如果亮度值大于 0.8, 亮度值减去边框临界值为正, 此时的透明度计算值会在 0~20 之间游走, 当然, 数值大于 1 的透明度值都会按照 1 渲染, 此时, 边框基本处于完全不透明状态, 加深的边框显现;
- border: .2em solid;
- border-color: rgba(calc(var(--red) - 50), calc(var(--green) - 50), calc(var(--blue) - 50), var(--border-alpha));
设置边框样式, 边框颜色比背景色深 50 个单位值 (负数为按照 0 渲染), 然后透明度就是基于亮度动态计算的. 深色背景下, 按钮边框透明度为 0, 不显示; 浅色背景下, 透明度在 0~1 之间游走, 出现, 北京颜色越浅, 边框透明度越大, 边框颜色越深, 符合配色预期.
相信经过上面的一番剖析, 大家就会明白其实现的原理了.
改变按钮的背景色
.btn 类名下的 CSS 代码是固定的, 让我们需要改变按钮的颜色的时候, 不是改. btn 下的 CSS, 而是修改: root 中的下面 3 个变量值:
- --red: 44;
- --green: 135;
- --blue: 255;
CSS 设置直接改数值, 如果是 JS 设置, 借助 setProperty() 方法, 若不了解, 可以参考这篇文章:"如何 HTML 标签和 JS 中设置 CSS3 var 变量 https://www.zhangxinxu.com/wordpress/?p=8163".
四, 最后结束语
由于 var 的兼容性限制, 这个非常有意思的 CSS 技巧还不太适合在大型对外项目中使用.
小程序可以一试, 因为内核环境相对固定. 内部系统, 实验项目可以玩一玩, 会很有意思.
这种配色技巧其实不仅可以用在按钮上, 一些大区域的布局也是可以用的, 因为这些布局的背景色可能是动态的 , 此时, 文字颜色的配色也可以借助 CSS 实现自动化.
另外, 本文 demo 中按钮文字就黑白两色, 实际上, 我们的相乘系数小一点的话, 可以有更多的色值出现, 配色会更加精致.
参考文章
Switch font color ... CSS
来源: http://www.tuicool.com/articles/eQrM3qe