CSS 颜色混合的N种方式
在项目中经常会碰到需要邻近色的场景,比如将一个颜色变深(浅)一点,下面是一个按钮的选中态
如果出现一种颜色就定义一个变量,每次都要维护多个颜色变量太麻烦了。有没有办法只用一个颜色呢?在这里也就是,如何将一个颜色变浅一点?这样做的好处是,如果需要更换主题色,只用修改一个变量就行了,如下
下面盘点我用过的一些方法
一、透明度
这应该是最容易想到的方式。将一个元素透明度降低不就颜色变浅了吗?
假设 HTML
是这样的(下同)
<button style="--primary-color: #3981E6">#3981E6</button>
不过这种方式需要借助单独一层标签,通常可以用伪元素生成,关键实现如下
button::before{
content: '';
background: var(--primary-color);
opacity: 0.2
}
效果还是非常不错的,下面是多种主题色的效果
不过这种方式也有局限,比如伪元素不够用怎么办?还有绝对定位引起的层级问题
没关系,还有其他方式,接着往下看
二、多重背景
大家可能都知道,CSS3 背景是支持多重背景的,并且层级是越来越低的
因此,我们可以在主题色上覆盖一层半透明的白色,依然可以将原有颜色“减淡”
由于这里是背景图,所以需要用到渐变,而不是颜色。比如希望主题色减淡到自身的20%
,可以覆盖80%
透明度的白色,实现如下
button{
background: linear-gradient(rgba(255,255,255,.8),rgba(255,255,255,.8)), /*半透明白色*/
linear-gradient(var(--primary-color), var(--primary-color));
}
这样就无需借助额外的标签或者伪元素了,效果如下
不过这种方式也有一些缺陷,比如仅适合背景层,如果希望box-shadow
、outline
这些就不行,这些属性没法叠加多层背景。
三、动画
这个方式在之前这篇文章中有详细介绍:
主要原理是动画播放次数也是支持小数的,比如设置一个从蓝色到白色的动画,播放次数为0.8
,那么在播放到80%
的地方就停下来了,这样就得到了颜色减淡的效果,示意如下
具体实现如下
button{
animation: lighterBackgroundColor .001s 0.8 linear forwards;
/*播放次数为0.8*/
}
@keyframes lighterBackgroundColor {
from {
background-color: var(--primary-color)
}
to{
background-color: #fff
}
}
效果也很棒
相比前一种方式,就没有背景的限制了,任意属性都可以,但是每出现一个属性就需要单独一个动画(因为动画变化的就是属性本身),如果要加一个减淡后的outline-color
,应该要这么实现
button{
animation: lighterBackgroundColor .001s 0.8 linear forwards,
lighterOutlineColor .001s 0.8 linear forwards; /*outline*/
/*播放次数为0.8*/
}
@keyframes lighterBackgroundColor {
from {
background-color: var(--primary-color)
}
to{
background-color: #fff
}
}
/*设置一个outline的动画*/
@keyframes lighterOutlineColor {
from {
outline-color: var(--primary-color)
}
to{
outline-color: #fff
}
}
太繁琐了,有没有简单一点的方法呢?
四、自定义属性动画
上面将属性作为动画有点浪费,因为变化值都是一样的,有没有办法复用呢?
当然可以,将 CSS 变量作为动画对象,比如--lighterColor
button{
animation: lighterColor .001s 0.8 linear forwards;
/*播放次数为0.8*/
}
@keyframes lighterColor {
from {
--lighterColor: var(--primary-color)
}
to{
--lighterColor: #fff
}
}
但是,仅仅这样是不够的,动画并不认识这样的变量,根本不会有动画(就像display
一样)
为了让自定义变量也支持动画,需要通过@property
定义一下
@property - CSS:层叠样式表 | MDN (mozilla.org)[1]
@property --lighterColor {
syntax: '<color>';
inherits: false;
initial-value: #fff;
}
相比前面的方式,但是适用性更佳,--lighterColor
已经是一个独立的变量了,可以用在任意属性上,比如加个outline
button{
background-color: var(--lighterColor);
outline: 4px solid var(--lighterColor);
}
可以看到,outline
也轻易地实现了颜色减淡
缺点就是,兼容性欠佳,目前firefox
还不支持
五、color-mix()
最后介绍一个最近正式支持(Chrome 110+)的颜色混合函数:color-mix()[2]
这个算是官方的解决方案了,如果这个普遍支持了,前面的方法都可以不用了,下面简单介绍一下
color-mix(in lch, peru 40%, lightgoldenrod);
color-mix(in srgb, #34c9eb 20%, white);
前面的in lch
表示色彩空间,我们一般只用srgb
就足够了,后面的两个颜色就需要混合的颜色了。
这里的百分比就是混合比例了,如果我们要实现减淡80%
的操作,可以将主题色的比例设置为20%
,白色会自动填充剩余比例,如下
button{
--lighterColor: color-mix(in srgb, var(--primary-color) 20%, #fff);
background-color: var(--lighterColor);
outline: 4px solid var(--lighterColor);
}
效果如下(Chrome 110+)
目前还不适合使用,过两年再说吧🙁
下面是所有方案的效果对比,基本是一致的
完整 demo 可以访问以下任意链接
CSS color lighter (juejin.cn)[3] CSS color lighter (codepen.io)[4] CSS color lighter (runjs.work)[5]
六、总结一下优缺点
以上共介绍了5种不同的颜色混合实现方式,各有优缺点,下面分别从以下几个方面比较一下
实现成本:实现思路的复杂度,是否容易想到 适应性:能否适应各种场景 代码复用性:实现是否啰嗦,是否需要额外标签 兼容性:能否大规模使用
实现成本 | 适应性 | 代码复用性 | 兼容性 | |
---|---|---|---|---|
透明度 | ⭐️(低) | ⭐️(差) | ⭐️(低) | ⭐️⭐️⭐️(好) |
多重背景 | ⭐️ | ⭐️ | ⭐️ | ⭐️⭐️⭐️ |
动画 | ⭐️⭐️⭐️(高) | ⭐️⭐️ | ⭐️⭐️ | ⭐️⭐️⭐️ |
自定义属性动画 | ⭐️⭐️ | ⭐️⭐️⭐️(强) | ⭐️⭐️⭐️ | ⭐️⭐️ |
color-mix | ⭐️ | ⭐️⭐️⭐️ | ⭐️⭐️⭐️(高) | ⭐️(差) |
总的来说,自定义属性动画在各方面是比较推荐的,如果不考虑firefox
的话基本可以放心使用了,其他方式也可以根据实际需求自行选择,哪个方便用哪个。
最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发❤❤❤
参考资料
@property - CSS:层叠样式表 | MDN (mozilla.org): https://developer.mozilla.org/zh-CN/docs/Web/CSS/@property
[2]color-mix(): https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix
[3]CSS color lighter (juejin.cn): https://code.juejin.cn/pen/7206605166953365563
[4]CSS color lighter (codepen.io): https://codepen.io/xboxyan/pen/dyqRyVG
[5]CSS color lighter (runjs.work): https://runjs.work/projects/91dcf9cdd9f7447b