mix-blend-mode
是一个非常有趣的CSS属性,它可以让你的元素与它的父元素进行混合,混合的子元素可以是任何元素,包括文本、图片、SVG等,接下来就和我一起来看看它的效果吧。
先看效果:
简介
什么是混合模式
在开始之前我们先来了解一下什么是混合模式,混合模式(Blend Mode)是图像处理中的一种技术,它可以让你的图像与背景图像进行混合,从而得到一个新的图像。
混合模式常见于图像处理软件中,比如PS(Photoshop)
、AI(Illustrator)
、AE(After Effects)
等。
常见的混合模式有:
- 正片叠底
- 叠加
- 柔光
- 颜色加深
- 颜色减淡
- 差值
- 反差
- 变暗
- 变亮
- 滤色
这些东西通常是需要图像处理软件来完成的,但是今天我们就可以在CSS中实现这些效果了。
什么是mix-blend-mode
mix-blend-mode
是CSS3
中新增的一个属性,它可以让你的元素与它的父元素进行混合,从而得到一个新的图像。
mix-blend-mode
的值有:
- normal:默认值,没有混合效果
- multiply:正片叠底
- screen:滤色
- overlay:叠加
- darken:变暗
- lighten:变亮
- color-dodge:颜色减淡
- color-burn:颜色加深
- hard-light:强光
- soft-light:柔光
- difference:差值
- exclusion:排除
- hue:色相
- saturation:饱和度
- color:颜色
- luminosity:亮度
可以看到这些值就是我们常见的混合模式,这就说明我们可以在CSS中实现这些效果了,接下来我们就来看看每种混合模式有什么区别。
混合模式的效果
正片叠底
正片叠底的作用是让两个图层的颜色相乘,从而得到一个新的图像,叠底就是让底层的颜色影响上层的颜色。
正片叠底是一种非常常见的混合模式,它的效果就是将两个图像的像素点的色值相乘,然后再除以255,得到的结果就是新的像素点的色值,这样就可以得到一个新的图像。
例如两个图像的像素点的色值分别为:
- 图像1:
#ff0000
- 图像2:
#00ff00
- 结果:
#000000
这样就可以得到一个新的图像,它的像素点的色值为#000000
。
在CSS中我们可以通过mix-blend-mode: multiply
来实现这种效果。
<div class="blend-mode"> <p class="multiply">正片叠底</p> </div> <style> .blend-mode { font-size: 24px; font-weight: 700; color: #ff0000; background: #00ff00; } p.multiply { mix-blend-mode: multiply; } </style>
这里用代码实现一下正片叠底的计算公式,大家可以自行验证是否正确。
function multiply(color1, color2) { const r1 = parseInt(color1.slice(1, 3), 16); const g1 = parseInt(color1.slice(3, 5), 16); const b1 = parseInt(color1.slice(5, 7), 16); const r2 = parseInt(color2.slice(1, 3), 16); const g2 = parseInt(color2.slice(3, 5), 16); const b2 = parseInt(color2.slice(5, 7), 16); const r = Math.round((r1 * r2) / 255); const g = Math.round((g1 * g2) / 255); const b = Math.round((b1 * b2) / 255); return `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`; } console.log(multiply('#ff0000', '#00ff00')); // #000
滤色
滤色的作用是将两个图层的之间的颜色叠加,使其产生一种比较明亮的效果。
滤色的效果计算相对来说要复杂一些,它的计算公式是:255 - ( (255-基色) * (255-混合色) ) / 255
例如两个图像的像素点的色值分别为:
- 混合色:
#ff0000
- 基色:
#00ff00
- 结果:
#ffff00
这样就可以得到一个新的图像,它的像素点的色值为#ffff00
。
在CSS中我们可以通过mix-blend-mode: screen
来实现这种效果。
/* 省略其他代码,同第一个示例代码相同 */ p.screen { mix-blend-mode: screen; }
代码实现:
function screen(baseColor, blendColor) { const r1 = parseInt(baseColor.slice(1, 3), 16); const g1 = parseInt(baseColor.slice(3, 5), 16); const b1 = parseInt(baseColor.slice(5, 7), 16); const r2 = parseInt(blendColor.slice(1, 3), 16); const g2 = parseInt(blendColor.slice(3, 5), 16); const b2 = parseInt(blendColor.slice(5, 7), 16); const r = Math.round(255 - ((255 - r1) * (255 - r2)) / 255); const g = Math.round(255 - ((255 - g1) * (255 - g2)) / 255); const b = Math.round(255 - ((255 - b1) * (255 - b2)) / 255); return `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`; } console.log(screen('#00ff00', '#ff0000')); // #ffff0
在代码中基色指的是父元素的背景色,混合色指的是字体的颜色。
叠加
叠加的效果是将两个图层的颜色叠加,使其产生一种比较明亮的效果,如果底部的颜色比较明亮则减弱顶部的颜色,如果底部的颜色比较暗则增强顶部的颜色。
叠加的计算公式更加复杂了,它分两种情况:
- 如果基色小于等于128,则结果为:基色 * 混合色 / 128
- 如果基色大于128,则结果为:255 - ( (255-基色) * (255-混合色) ) / 128
例如两个图像的像素点的色值分别为:
- 混合色:
#ff0000
- 基色:
#00ff00
- 结果:
#00ff00
这样就可以得到一个新的图像,它的像素点的色值为#00ff00
。
在CSS中我们可以通过mix-blend-mode: overlay
来实现这种效果。
p.overlay { mix-blend-mode: overlay; }
代码实现:
function overlay(baseColor, blendColor) { const r1 = parseInt(baseColor.slice(1, 3), 16); const g1 = parseInt(baseColor.slice(3, 5), 16); const b1 = parseInt(baseColor.slice(5, 7), 16); const r2 = parseInt(blendColor.slice(1, 3), 16); const g2 = parseInt(blendColor.slice(3, 5), 16); const b2 = parseInt(blendColor.slice(5, 7), 16); const r = r1 <= 128 ? Math.round((r1 * r2) / 128) : Math.round(255 - ((255 - r1) * (255 - r2)) / 128); const g = g1 <= 128 ? Math.round((g1 * g2) / 128) : Math.round(255 - ((255 - g1) * (255 - g2)) / 128); const b = b1 <= 128 ? Math.round((b1 * b2) / 128) : Math.round(255 - ((255 - b1) * (255 - b2)) / 128); return `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`; } console.log(overlay('#00ff00', '#ff0000')); // #0ff0
变暗、
变暗的效果是将两个图层的颜色进行比较,如果混合色比基色暗,则使用混合色,否则使用基色。
变暗的计算公式是:基色和混合色中较小的那个会被保留,另一个会被抛弃。
例如两个图像的像素点的色值分别为:
- 混合色:
#ff0000
- 基色:
#00ff00
- 结果:
#000000
这样就可以得到一个新的图像,它的像素点的色值为#000000
。
在CSS中我们可以通过mix-blend-mode: darken
来实现这种效果。
p.darken { mix-blend-mode: darken; }
代码实现:
function darken(baseColor, blendColor) { const r1 = parseInt(baseColor.slice(1, 3), 16); const g1 = parseInt(baseColor.slice(3, 5), 16); const b1 = parseInt(baseColor.slice(5, 7), 16); const r2 = parseInt(blendColor.slice(1, 3), 16); const g2 = parseInt(blendColor.slice(3, 5), 16); const b2 = parseInt(blendColor.slice(5, 7), 16); const r = Math.min(r1, r2); const g = Math.min(g1, g2); const b = Math.min(b1, b2); return `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`; } console.log(darken('#ff0000', '#00ff00')); // #000
变亮
变亮和变暗正好相反。
变亮的计算公式是:基色和混合色中较大的那个会被保留,另一个会被抛弃。
例如两个图像的像素点的色值分别为:
- 混合色:
#ff0000
- 基色:
#00ff00
- 结果:
#ffff00
这样就可以得到一个新的图像,它的像素点的色值为#ffff00
。
在CSS中我们可以通过mix-blend-mode: lighten
来实现这种效果。
p.lighten { mix-blend-mode: lighten; }
代码实现:
function lighten(baseColor, blendColor) { const r1 = parseInt(baseColor.slice(1, 3), 16); const g1 = parseInt(baseColor.slice(3, 5), 16); const b1 = parseInt(baseColor.slice(5, 7), 16); const r2 = parseInt(blendColor.slice(1, 3), 16); const g2 = parseInt(blendColor.slice(3, 5), 16); const b2 = parseInt(blendColor.slice(5, 7), 16); const r = Math.max(r1, r2); const g = Math.max(g1, g2); const b = Math.max(b1, b2); return `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`; } console.log(lighten('#ff0000', '#00ff00')); // #ffff0
颜色减淡
颜色减淡的效果是将混合色的颜色添加到基色上,但是如果混合色的颜色比基色的颜色要深,则会使基色的颜色变浅。
颜色加深的计算公式是:基色 / (1 - 混合色)
例如两个图像的像素点的色值分别为:
- 混合色:
#ff0000
- 基色:
#00ff00
- 结果:
#00ff00
这样就可以得到一个新的图像,它的像素点的色值为#00ff00
。
在CSS中我们可以通过mix-blend-mode: color-dodge
来实现这种效果。
p.color-dodge { mix-blend-mode: color-dodge; }
代码实现:
function computed(Cb, Cs) { let B; if (Cb == 0) { B = 0; } else if (Cs == 1) { B = 1; } else { B = Math.min(1, Cb / (1 - Cs)); } return Math.round(B * 255); } function colorDodge(baseColor, blendColor) { const r1 = parseInt(baseColor.slice(1, 3), 16); const g1 = parseInt(baseColor.slice(3, 5), 16); const b1 = parseInt(baseColor.slice(5, 7), 16); const r2 = parseInt(blendColor.slice(1, 3), 16); const g2 = parseInt(blendColor.slice(3, 5), 16); const b2 = parseInt(blendColor.slice(5, 7), 16); const r = computed(r1 / 255, r2 / 255); const g = computed(g1 / 255, g2 / 255); const b = computed(b1 / 255, b2 / 255); return `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`; } console.log(colorDodge('#00ff00', '#ff0000')); // #0ff0
这次的公式有点奇怪,1 代表 255,0 代表 0,因为我找了很多资料,这个公式是靠谱的,所以就沿用0和1的计算方式。
颜色加深
颜色加深的效果是将混合色的颜色添加到基色上,但是如果混合色的颜色比基色的颜色要浅,则会使基色的颜色变深。
颜色加深的计算公式是:1 - (1 - 基色) / 混合色
例如两个图像的像素点的色值分别为:
- 混合色:
#ff0000
- 基色:
#00ff00
- 结果:
#000000
这样就可以得到一个新的图像,它的像素点的色值为#000000
。
在CSS中我们可以通过mix-blend-mode: color-burn
来实现这种效果。
p.color-burn { mix-blend-mode: color-burn; }
颜色加深的代码实现:
function computed(Cb, Cs) { let B; if (Cb == 1) { B = 1; } else if (Cs == 0) { B = 0; } else { B = Math.max(0, 1 - (1 - Cb) / Cs); } return Math.round(B * 255); } function colorBurn(baseColor, blendColor) { const r1 = parseInt(baseColor.slice(1, 3), 16); const g1 = parseInt(baseColor.slice(3, 5), 16); const b1 = parseInt(baseColor.slice(5, 7), 16); const r2 = parseInt(blendColor.slice(1, 3), 16); const g2 = parseInt(blendColor.slice(3, 5), 16); const b2 = parseInt(blendColor.slice(5, 7), 16); const r = computed(r1 / 255, r2 / 255); const g = computed(g1 / 255, g2 / 255); const b = computed(b1 / 255, b2 / 255); return `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`; } console.log(colorBurn('#00ff00', '#ff0000')); // #0ff0
强光
强光主要用于调整图像的对比度和亮度,当混合色比较亮时,强光模式会让图像更亮,反之则会让图像变暗。
强光的计算公式是:混合色 < 0.5 ? 2 * 基色 * 混合色 : 1 - 2 * (1 - 基色) * (1 - 混合色)
例如两个图像的像素点的色值分别为:
- 混合色:
#ff0000
- 基色:
#00ff00
- 结果:
#ff0000
这样就可以得到一个新的图像,它的像素点的色值为#ff0000
。
在CSS中我们可以通过mix-blend-mode: hard-light
来实现这种效果。
p.hard-light { mix-blend-mode: hard-light; }
强光的代码实现:
function computed(Cb, Cs) { let B; if (Cs < 0.5) { B = 2 * Cb * Cs; } else { B = 1 - 2 * (1 - Cb) * (1 - Cs); } return Math.round(B * 255); } function hardLight(baseColor, blendColor) { const r1 = parseInt(baseColor.slice(1, 3), 16); const g1 = parseInt(baseColor.slice(3, 5), 16); const b1 = parseInt(baseColor.slice(5, 7), 16); const r2 = parseInt(blendColor.slice(1, 3), 16); const g2 = parseInt(blendColor.slice(3, 5), 16); const b2 = parseInt(blendColor.slice(5, 7), 16); const r = computed(r1 / 255, r2 / 255); const g = computed(g1 / 255, g2 / 255); const b = computed(b1 / 255, b2 / 255); return `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`; } console.log(hardLight('#00ff00', '#ff0000')); // #ff00
柔光
在柔光模式下,如果顶部图层的像素颜色值比底部图层的像素颜色值大,则将其混合到底部图层中;反之,则减少底部图层的亮度。
柔光的计算公式是:混合色 < 0.5 ? 2 * 基色 * 混合色 + 基色 * 基色 * (1 - 2 * 混合色) : sqrt(基色) * (2 * 混合色 - 1) + 2 * 基色 * (1 - 混合色)
例如两个图像的像素点的色值分别为:
- 混合色:
#ff0000
- 基色:
#00ff00
- 结果:
#00ff00
这样就可以得到一个新的图像,它的像素点的色值为#00ff00
。
柔光的代码实现:
function computed(Cb, Cs) { let B; if (Cs < 0.5) { B = 2 * Cb * Cs + Cb * Cb * (1 - 2 * Cs); } else { B = Math.sqrt(Cb) * (2 * Cs - 1) + 2 * Cb * (1 - Cs); } return Math.round(B * 255); } function softLight(baseColor, blendColor) { const r1 = parseInt(baseColor.slice(1, 3), 16); const g1 = parseInt(baseColor.slice(3, 5), 16); const b1 = parseInt(baseColor.slice(5, 7), 16); const r2 = parseInt(blendColor.slice(1, 3), 16); const g2 = parseInt(blendColor.slice(3, 5), 16); const b2 = parseInt(blendColor.slice(5, 7), 16); const r = computed(r1 / 255, r2 / 255); const g = computed(g1 / 255, g2 / 255); const b = computed(b1 / 255, b2 / 255); return `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`; } console.log(softLight('#00ff00', '#ff0000')); // #0ff0
差值
排除
色相
饱和度
颜色
亮度
写累了,不想写了,有兴趣的可以自己去实现一下,这些都在规范里面有:ltblendmodegt
后面几个感兴趣的可以自己动手来实现一下代码,这里就不写了。
使用
其实上面的原理没多少会喜欢,大家还是喜欢看实际的效果,那么我们就来看看这些混合模式的实际效果。
首先我上面的示例都是作用在文本上的,实际这些混合模式是可以作用在任何元素上的,比如图片,背景等等。
并且这些元素都是会作为叠加层来进行混合的,也就是说,如果你的元素有背景色,那么这个背景色也会参与到混合中,只要对顶层元素设置了混合模式,那么它的背景色也会参与到混合中。
我们来看看字体颜色与背景色的叠加,这个是最简单的,我们只需要设置一个背景色,然后设置一个字体颜色,然后设置一个混合模式就可以了。
<div class="blend-mode"> <span>A</span> <span>B</span> <span>C</span> <span>D</span> </div> <style> .blend-mode { width: 200px; padding: 20px; font-weight: 700; font-size: 80px; letter-spacing: -.3em; text-align: center; color: #ff0000; background: linear-gradient(45deg, #F53F3F, #F77234, #FF7D00, #F7BA1E, #FADC19, #9FDB1D, #00B42A, #14C9C9, #3491FA, #165DFF, #722ED1); } .blend-mode span:nth-child(1) { color: #ff0000; mix-blend-mode: multiply; } .blend-mode span:nth-child(2) { color: #ffff00; mix-blend-mode: difference; } .blend-mode span:nth-child(3) { color: #00ff00; mix-blend-mode: color-dodge; } .blend-mode span:nth-child(4) { color: #0000ff; mix-blend-mode: screen; } </style>
可以看到这样的叠加效果是非常有趣的,这里我只是用了四种混合模式,可以根据自己的需要进行搭配和选择。
我上面的效果虽然说还算可以,但是并没有那么有趣,那就随随便便加点动画,好像突然就变的有趣起来了:
.blend-mode span { display: inline-block; } .blend-mode span:nth-child(1) { color: #ff0000; mix-blend-mode: multiply; animation: shake 1.3s infinite alternate; animation-delay: .3s; } .blend-mode span:nth-child(2) { color: #ffff00; mix-blend-mode: difference; animation: shake 1s infinite alternate; animation-delay: .6s; } .blend-mode span:nth-child(3) { color: #00ff00; mix-blend-mode: color-dodge; animation: shake 1.5s infinite alternate; animation-delay: .9s; } .blend-mode span:nth-child(4) { color: #0000ff; mix-blend-mode: screen; animation: shake 2s infinite alternate; animation-delay: 1.2s; } @keyframes shake { 0% { transform: translate(0, -10px); color: #3491FA; } 20% { color: #722ED1; } 40% { color: #F5319D; } 60% { color: #A0C95E; } 80% { color: #EBD89A; } 100% { transform: translate(0, 20px); } }
总结
mix-blend-mode
是一个非常有趣的属性,也是非常强大的一个属性,它可以让我们的元素有更多的表现力,让我们的元素更加有趣,更加有生命力。
我上面的案例都是使用的文字相关的效果,但是这个属性并不仅仅局限于文字,它可以作用在任何元素上,比如图片,背景等等,只要对顶层元素设置了混合模式,那么它的背景色也会参与到混合中。
这个属性的兼容性还是很好的,除了IE11以下的版本,其他的浏览器都是支持的,所以可以放心的使用。