混合模式,这个CSS3的新特性,这个特性的问世已经有好几年了,但是我一直很少尝试使用,之所以很少使用,主要是因为缺乏业务场景使用,还有一个重要的原因就是我虽然知道有这个特性,但是对其背后的原理和逻辑一直很模糊,尤其对于一个 Photoshop 都不太熟悉的人,更难上手这些东西的使用了。以下是 Photoshop 图层混合模式的概览,让我们先大概对混合模式有个大概的印象。
当我去想了解清楚到底什么是混合模式?网上找了一堆文章,虽然也罗列了一些示例和说明,但是背后的逻辑也是让我稀里糊涂的。今天看到一些文章和示例,觉得很酷,我觉得有必要了解清楚,因此通过归纳的形式让自己梳理清楚,彻底弄明白。
在CSS中,由于定位,两个元素可能出现部分重叠,前面的部分会遮住后面的部分,如果有 alpha 通道,而且小于1,我们就能看到后面的元素。熟悉 Photoshop 的朋友,重叠的图层有多重混合模式,现在 CSS 提供了这样的混合策略。目前提供了两种策略:混合元素(mix-blend-mode) 和 背景混合(background-blend-mode)。
在介绍这前,我们先了解混合的逻辑是什么?
混合意味着组合两层(堆叠在另一层之上)并得到一个单层。这两个层可以是两个兄弟元素,在这种情况下,我们使用的 CSS 属性是 mix-blend-mode。它们也可以是两个背景层,在这种情况下,我们使用的 CSS 属性是 background-blend-mode。请注意,当我谈论混合“兄弟”时,这包括将元素与伪元素或文本内容或其父元素的背景混合。当涉及到背景层时,我所说的不仅仅是背景图像层——背景颜色也是一个层。
混合两个图层时,上面的图层称为源图层,而下面的图层称为目标图层。这些名字没有多大意义,只是我们在介绍逻辑的时候比较方便。至少对我们来说,这个层是输入,我们关注结果如何输出而已。
为了解释方便,我们暂且称源图层称作顶层,模板图层称作底层。
我们如何精确地组合两层取决于所使用的特定混合模式,但它总是按像素计算。例如,下图使用multiply blend mode 来组合两个图层,表示为像素网格。
如果多于两个图层,该如何混合呢?其逻辑将从最后一个图层开始,两两混合,得到的混合结果,再与上一个图层再次合成,直到最上面一个图层为止。可能你还不太理解,看完下图,你就会明白了。
我们可以使用不同的混合模式,比如最底下的两个图层使用 difference 模式得到的结果,再与第三个图层采用 multiply 模式混合。
关于图层通道,合成的红色通道仅取决于源红色通道和目标的红色通道;生成的绿色通道仅取决于源的绿色通道和目标的绿色通道,蓝色通道也是如此,我们可以用如下公式进行表示:
R = fB(Rs, Rd) G = fB(Gs, Gd) B = fB(Bs, Bd)
需要记住的是,RGB 值可以在 [0, 255] 区间中表示,也可以用百分比进行表示[0%, 100%] ,比如rgb(220, 20, 60)
可以用 rgb(86.3%, 7.8%, 23.5%)
表示,这两个是等效的。黑色 rgb(0, 0, 0)
可以用 rgb(0%, 0%, 0%)
,白色rgb(255, 255, 255)
可以用 rgb(100%, 100%, 100%)
。
前面已经说过,混合元素的模式将顶层与底层合并。层可以是父元素、根元素或堆栈中目标元素下方的元素的背景颜色或图像。我们来看下段代码,HTML部分如下段代码所示:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>mix-blend-mode</title> <link rel="stylesheet" href="mix-blend-mode.css"> </head> <body> <div> <p>This is a paragraph that's positioned below the image in the stack. </p> <img src="dahlia.jpg"> </div> </body> </html>
mix-blend-mode.css 部分的代码如下所示:
div { background-image: linear-gradient(to left, #f30, #fc0); width: 60vw; margin: 3rem auto; position: relative; } p { position: absolute; color: black; margin: 4rem; font-size: 6.4rem; } img { display: block; height: auto; width: 100%; }
最终完成后的效果如下所示:
请注意,上图中的文字很难阅读。那是因为文本和图像之间的对比度不足。在实际项目中,您应该为 p 元素添加背景颜色或渐变以使其清晰易读。还要确保前景色和背景色之间有足够的对比度。
完成上述代码后,我们开始设置混合模式的属性。
由于 p 元素是绝对定位的,因此文本会覆盖照片。还要注意,照片填充了其父容器的整个宽度和高度,因此我们看不到它的背景渐变。现在让我们将 mix-blend-mode 添加 img 混合模式的属性。这里我们使用 difference 差值模式看下效果。
该混合模式会查看每个通道中的颜色信息,比较底层和上层,用较亮的像素点的像素值减去较暗的像素点的像素值。 与白色混合将使底色反相(补色);与黑色混合则不产生颜色变化。用数学公式表达就是两个值相减后的绝对值。
Ch = fB(Chs, Chd) = |Chs - Chd|
如下图所示,顶层图层分别为黑、白、 渐变色图层。 底层为与顶层同样的渐变图层,如下图所示:
合成后的效果
合成后效果,与黑色合成为底层原本的颜色,与白色合成则为对应颜色的补色(或反向),同样的颜色差异则为黑色。
解释到这里,我们接着上面的代码,设置图片的混合属性为 difference,示例代码如下:
img { display: block; height: auto; width: 100%; mix-blend-mode: difference; }
现在照片已移至图层堆栈的顶部(如下图所示),与段落文本及其父 div 的背景图像混合。
排除模式,相对差值模式,相对更加温和,因为其数学公式如下所示,两个相加,
Ch = fB(Chs, Chd) = Chs + Chd - 2·Chs·Chd
如果顶层为黑色,合成后的颜色,为底层的颜色,从数学公式中可以看出:
Ch = fB(0, Chd) = 0 + Chd - 2·0·Chd = Chd - 0 = Chd
如果底层为黑色,合成后的颜色,为顶层的颜色:
Ch = fB(Chs, 0) = Chs + 0 - 2·Chs·0 = Chs - 0 = Chs
如果顶层或底层为白色的话,合成的颜色,则为对应颜色的补色:
Ch = fB(1, Chd) = 1 + Chd - 2·1·Chd = 1 + Chd - 2·Chd = 1 - Chd Ch = fB(Chs, 1) = Chs + 1 - 2·Chs·1 = Chs + 1 - 2·Chs = 1 - Chs
为了更好的理解,我们还是用下图来解释下,如果顶层分别为黑色、白色,如下图所示:
使用排除的方式,合成的效果如下图所示:
介绍完了差值和排除属性模式,我们看个示例,看看在实际中如何应用。
首先我们在HTML文件里,定义一段文本,如下所示
<p>Hello, <a href='#'>World</a>!</div>
接下来,我们首先设置一些基本样式,将文本放在屏幕中间,增大字体大小,在正文上设置背景,在段落和链接上设置颜色。
body { display: grid; place-content: center; height: 100vh; background: #222; color: #ddd; font-size: clamp(1.25em, 15vw, 7em); } a { color: gold; }
完成后的效果如下图所示:
接下来,我们使用 ::after 伪元素在黄色链接上,定义个与文本颜色相同的背景颜色进行覆盖,示例代码如下:
a { position: relative; color: gold; &::after { position: absolute; top: 0; bottom: 0; right: 0; left: 0; background: currentColor; content: ''; } }
链接被蒙上黄色背景后的效果:
接下来,你应该拆到我该做什么了,不至于一个黄色的背景覆盖在链接文字上,我们应该使用混合模式的特性,使用差值(difference),底层和顶层颜色一致时,混合后,重合的部分则显示黑色。修改后的代码如下:
p { isolation: isolate; } a { /* same as before */ &::after { /* same as before */ mix-blend-mode: difference; } }
如你所料,完成后的效果如下图所示:
接下来我们做一些动画效果,使用伪元素 hover 经过效果,让链接上的背景色由隐藏到显示,这里我们使用 scale(0) 让背景隐藏。
a { /* same as before */ text-decoration: none; &::after { /* same as before */ transform: scale(0); } &:focus { outline: none } &:focus, &:hover { &::after { transform: none; } } }
上述代码我们删除了链接的下划线和选中后的边框,鼠标经过后的效果,如下图所示:
上述的动画效果还不够平滑,我们需要添加 transition 属性,让其更加平滑:
a { /* same as before */ &::after { /* same as before */ transition: transform .25s; } }
如下图所示,效果是不是更好呢:
由于背景的默认中心点在中央,所以效果是从中间向两边发散,接下来我们来更改效果,让你背景色由底部出现的效果,更改背景的中心点在底部,如下段代码所示:
a { /* same as before */ &::after { /* same as before */ transform-origin: 0 100%; transform: scaleY(.05); } }
更改后的效果如下图所示,效果更加自然,由底部往上推的效果
由于篇幅原因,今天的文章就介绍到这里,接下来的文章,我们继续学习背景混合模式,敬请期待…
注:本文属于原创文章,版权属于「前端达人」公众号及 qianduandaren.com 所有,未经授权,谢绝一切形式的转载