揭秘CSS滤镜的神秘面纱

发表时间: 2024-05-04 10:07

1 前言

在日常开发中,对于图像不同效果的处理,通常需要设计侧的专业知识和技能,同时开发侧也需要增加额外的图片资源的请求。为了避免引入多个图片资源且和增加一些额外的配置项去实现不同效果的处理,我们可以使用CSS的滤镜属性直接对元素进行效果处理,而不需要对每个元素的不同效果引入相关的图片文件,从而能提高更大的灵活性和可维护性。



2 背景

在开发过程中我们总会碰到这样的场景:符合条件时按钮或图片高亮展示,不符合条件时按钮或图片置灰展示。

我们可能很自然地就去想这无非就是用两种样式:高亮的和非高亮的样式,两种样式对应的按钮颜色或者图片路径不同。那么要是每个按钮或图片都不一样,我们就需要对每一个高亮的场景都去做一个灰色场景的处理,即便是走配置化的数据,那么我们也都需要配置两份数据去做展示,这样不仅配置麻烦且工作效率较低。

如果我们只想要配置一套数据的话,其实可以使用filter滤镜去进行实现,我们可以利用其grayscale属性,将配置好高亮图片转换为灰度图像进行实现,参考效果如下图所示。

图1 奖励领取置灰效果图

filter滤镜就是css3新增的一个神奇功能,它可以对网页中的图片进行类似Photoshop图片处理的效果,但并不需要依赖于任何作图软件。同时,filter滤镜不仅可以对图片添加滤镜效果,还可以对网页元素甚至视频进行滤镜处理。



3 基本概念

filter属性通过调整滤镜函数的参数,可以改变元素的颜色、透明度、亮度、对比度等视觉特性。

  • 作用对象:使用该属性的元素本身。
  • 使用方式:filter函数 <filter-function> 或使用url添加的svg滤镜。
filter: <filter-function> [<filter-function>]* | nonefilter: url(file.svg#filter-element-id)


3.1 基础函数

3.1.1 blur

blur(radius)函数将高斯模糊应用到图像。radius 定义了高斯函数的标准偏差值,或者屏幕上有多少像素相互融合,值越大模糊程度越高。若未设置值,默认为 0。

图2 blur滤镜效果对比图


3.1.2 brightness

brightness(amount)函数让图像更明亮或更暗淡。值为 0% 将创建全黑图像,值为 100% 会使输入保持不变,值大于 100% 提供更明亮的结果。若未设置值,默认为 1。

图3 brightness滤镜效果对比图

3.1.3 contrast

contrast(amount)函数可调整输入图像的对比度。值是 0% 图像会全黑,值为100% 图像不变,值可以超过 100%,意味着会运用更低的对比。若未设置值,默认是 1。

图4 contrast滤镜效果对比图


3.1.4 drop-shadow

drop-shadow(offset-x offset-y blur-radius color) 可在图像后方应用投影。阴影可以设置模糊度,以特定颜色画出的遮罩图的偏移版本,最终合成在图像下面。

图5 drop-shadow滤镜效果对比图


3.1.5 grayscale

grayscale(amount)函数可将图像转为灰度图。amount的值定义了转换的比例,值在 0% 到 100% 之间,值为 0% 图像无变化,值为 100%则完全转为灰度图像。若未设置值,默认是 0。

图6 grayscale滤镜效果对比图


3.1.6 hue-rotate

hue-rotate(angle)函数可在图像应用色相旋转,angle的值设定图像会被调整的色环角度值。值为 0deg,则图像无变化,超过 360deg相当于又绕一圈。若未设置值,默认为 0deg。

图7 hue-rotate滤镜效果对比图


3.1.7 invert

invert(amount)函数可反转图像颜色。amount的值定义转换的比例,值在 0% 和 100% 之间,值为 0% 则图像无变化,值为 100% 则图像完全反转。若未设置值,则默认为 0。

图8 invert滤镜效果对比图


3.1.8 opacity

opacity(amount) 函数可改变图像透明度。amount的值定义转换的比例,值在 0% 和 100% 之间,值为 0% 则是完全透明,值为 100% 则图像无变化。若未设置值,则默认为 1。该函数与css的opacity 属性很相似,不同之处在于通过 filter,一些浏览器为了提升性能会提供硬件加速。

图9 opacity滤镜效果对比图


3.1.9 saturate

saturate(amount)函数可转换图像饱和度。amount的值定义转换的比例,值为 0% 则是完全不饱和,值为 100% 则图像无变化,超过 100% 则有更高的饱和度。若未设置值,则默认为 1。

图10 saturate滤镜效果对比图


3.1.10 sepia

sepia(amount) 函数可将图像转换为深褐色。amount的值定义转换的比例,值在 0% 到 100% 之间,值为 0% 图像无变化,值为 100% 则完全是深褐色的。若未设置值,则默认为 0。

图11 sepia滤镜效果对比图


3.2 复合函数

从filter函数的使用方式可以看出,滤镜效果是可以进行叠加的,比如,filter: contrast(50%) brightness(50%)。

图12 组合滤镜效果图

那么同一元素上定义多个滤镜的话,滤镜的先后顺序对最后效果有影响吗?我们调换滤镜顺序换成filter: brightness(50%) contrast(50%)来看看。

图13 组合滤镜顺序变换效果图

由上图可以看出,滤镜的先后顺序不同产生的效果也是不一样的。原因在于滤镜顺序不同,滤镜的色值处理算法对图片处理的先后顺序也就不一样,最终产生的效果就不一样。

其实CSS滤镜不仅可以组合使用实现很多视觉效果,也可以通过结合不同的滤镜效果和CSS动画,去实现一些奇妙的动画效果。



4 动画效果

4.1 文字融合动画

文字融合动画就是一种文字从融合到分开的效果,我们可以通过改变letter-spacing和blur的值去进行效果实现。

  <div class="container">    <div class="text">Hello World</div>  </div>
.container {  width: 100vw;  height: 100vh;  display: flex;  justify-content: center;  align-items: center;  background-color: #000;  filter: contrast(30);}.text {  color: #fff;  font-size: 100px;  animation: show 3s linear forwards;}@keyframes show {  0% {    opacity: 0;    letter-spacing: -50px;    filter: blur(10px);  }  25% {    opacity: 1;    filter: blur(8px);  }  50% {    filter: blur(5px);  }  100% {    letter-spacing: 10px;    filter: blur(2px);  }}


,时长00:03


4.2 手机充电动画

该动画其实就是一种模拟安卓手机充电时的动画效果,主要就是通过contrast滤镜以及blur滤镜去实现很好的融合效果。

<div class="g-container">  <div class="g-number">90%</div>  <div class="g-contrast">    <div class="g-circle"></div>    <ul class="g-bubbles">      <li></li>      <li></li>      <li></li>      <li></li>      <li></li>      <li></li>      <li></li>      <li></li>      <li></li>      <li></li>      <li></li>      <li></li>      <li></li>      <li></li>      <li></li>    </ul>  </div></div>
.g-container {  position: relative;  width: 300px;  height: 400px;  margin: auto;}.g-number {  position: absolute;  top: 100px;  width: 100%;  color: #fff;  font-size: 30px;  text-align: center;  z-index: 10;}.g-contrast {  width: 100%;  height: 100%;  background-color: #000;  overflow: hidden;  filter: contrast(10) hue-rotate(0);  animation: hueRotate 10s infinite linear;}.g-circle {  position: relative;  width: 300px;  height: 300px;  box-sizing: border-box;  filter: blur(8px);}.g-circle::after {  content: "";  position: absolute;  top: 40%;  left: 50%;  transform: translate(-50%, -50%) rotate(0);  width: 200px;  height: 200px;  background-color: #00ff6f;  border-radius: 42% 38% 62% 49%/45%;  animation: rotate 10s infinite linear;}.g-circle::before {  content: "";  position: absolute;  width: 176px;  height: 176px;  top: 40%;  left: 50%;  transform: translate(-50%, -50%);  border-radius: 50%;  background-color: #000;  z-index: 10;}.g-bubbles {  position: absolute;  left: 50%;  bottom: 0;  width: 100px;  height: 40px;  transform: translate(-50%, 0);  border-radius: 100px 100px 0 0;  background-color: #00ff6f;  filter: blur(5px);}li {  position: absolute;  border-radius: 50%;  background: #00ff6f;}li:nth-child(0) {  left: 50px;  top: 50%;  transform: translate(-50%, -50%);  width: 16px;  height: 16px;  animation: move 4s ease-in-out -1.47s infinite;}li:nth-child(1) {  left: 55px;  top: 50%;  transform: translate(-50%, -50%);  width: 18px;  height: 18px;  animation: move 6s ease-in-out -2.23s infinite;}li:nth-child(2) {  left: 60px;  top: 50%;  transform: translate(-50%, -50%);  width: 20px;  height: 20px;  animation: move 5s ease-in-out -3.89s infinite;}li:nth-child(3) {  left: 65px;  top: 50%;  transform: translate(-50%, -50%);  width: 22px;  height: 22px;  animation: move 7s ease-in-out -4.56s infinite;}li:nth-child(4) {  left: 70px;  top: 50%;  transform: translate(-50%, -50%);  width: 24px;  height: 24px;  animation: move 9s ease-in-out -2.12s infinite;}li:nth-child(5) {  left: 75px;  top: 50%;  transform: translate(-50%, -50%);  width: 26px;  height: 26px;  animation: move 9s ease-in-out -1.98s infinite;}li:nth-child(6) {  left: 80px;  top: 50%;  transform: translate(-50%, -50%);  width: 23px;  height: 23px;  animation: move 5s ease-in-out -3.35s infinite;}li:nth-child(7) {  left: 63px;  top: 50%;  transform: translate(-50%, -50%);  width: 19px;  height: 19px;  animation: move 7s ease-in-out -4.34s infinite;}li:nth-child(8) {  left: 67px;  top: 50%;  transform: translate(-50%, -50%);  width: 25px;  height: 25px;  animation: move 6s ease-in-out -2.87s infinite;}li:nth-child(9) {  left: 73px;  top: 50%;  transform: translate(-50%, -50%);  width: 28px;  height: 28px;  animation: move 8s ease-in-out -3.45s infinite;}li:nth-child(10) {  left: 56px;  top: 50%;  transform: translate(-50%, -50%);  width: 24px;  height: 24px;  animation: move 7s ease-in-out -1.83s infinite;}li:nth-child(11) {  left: 45px;  top: 50%;  transform: translate(-50%, -50%);  width: 25px;  height: 25px;  animation: move 5s ease-in-out -2.67s infinite;}li:nth-child(12) {  left: 68px;  top: 50%;  transform: translate(-50%, -50%);  width: 19px;  height: 19px;  animation: move 6s ease-in-out -3.12s infinite;}li:nth-child(13) {  left: 54px;  top: 50%;  transform: translate(-50%, -50%);  width: 29px;  height: 29px;  animation: move 9s ease-in-out -2.22s infinite;}li:nth-child(14) {  left: 41px;  top: 50%;  transform: translate(-50%, -50%);  width: 27px;  height: 27px;  animation: move 5s ease-in-out -1.11s infinite;}li:nth-child(15) {  left: 51px;  top: 50%;  transform: translate(-50%, -50%);  width: 24px;  height: 24px;  animation: move 7s ease-in-out -3.23s infinite;}@keyframes rotate {  50% {    border-radius: 45%/42% 38% 58% 49%;  }  100% {    transform: translate(-50%, -50%) rotate(720deg);  }}@keyframes move {  90% {    opacity: 1;  }  100% {    opacity: 0.1;    transform: translate(-50%, -180px);  }}@keyframes hueRotate {  100% {    filter: contrast(15) hue-rotate(360deg);  }}

图14 手机充电动画效果图



4.3 电影谢幕动画

电影谢幕动画其实就是图片逐渐消失而谢幕文案逐渐模糊显示的效果,我们可以使用filter属性中的blur和brightness滤镜来模拟该效果的实现。

<div class="main">  <div class="img"></div>  <div class="text">Thank you for watching!</div></div>
.main {  width: 100vw;  height: 100vh;  display: flex;  align-items: center;  justify-content: center;  background-color: #000;}.img {  position: absolute;  top: 0;  left: 0;  height: 100%;  width: 100%;  background: url("xxx.png");  background-size: cover;  animation: imgHide 3s linear forwards;}.text {  color: #fff;  font-size: 50px;  opacity: 0;  animation: textShow 2s linear forwards;}@keyframes imgHide {  0% {    filter: brightness(1);  }  100% {    filter: brightness(0);  }}@keyframes textShow {  0% {    opacity: 0;    filter: blur(8px) brightness(50%);  }  100% {    opacity: 1;    filter: blur(0) brightness(100%);  }}


,时长00:03


4.4. 彩虹文字动画

彩虹文字动画其实就是一种动态变化文字颜色的效果。该动画效果的实现就是通过动态地改变hue-rotate的值,使文字颜色在一定范围内进行循环变化产生动态颜色。

 <div>MAINTO MEMBER CENTER</div>
div {  font-size: 80px;  background-clip: text;  -webkit-background-clip: text;  -webkit-text-fill-color: transparent;  background-image: linear-gradient(to right, red, orange, yellow, green, cyan, blue, purple);  animation: huerotate 6s linear infinite;}@keyframes huerotate {  from { filter: hue-rotate(0deg); }  to { filter: hue-rotate(360deg); }}

效果图片如下所示:

图15 彩虹文字动画效果图

同样我们也可以通过这种方式去实现一种动态背景颜色变换的效果。

div {    background: linear-gradient(45deg, #5fddcc, #ff004d);    animation: hueRotate 10s infinite alternate;}@keyframes hueRotate {    0 {        filter: hue-rotate(0);    }    100% {        filter: hue-rotate(360deg);    }}


图16 动态背景颜色变换效果图

这些通过CSS动画或JavaScript控制去实时改变元素上的滤镜效果,从而在页面上产生动态变化的视觉效果又叫做动态滤镜效果,那与之相对应的不会发生改变或动态变化自然就是静态滤镜效果。静态滤镜效果在开发中经常被使用,下面我们就来聊聊在日常营销业务中的一些常见的静态滤镜的使用场景。



5 业务场景

5.1 网页置灰效果

基于一些特殊时期或者特定重大事件的背景下,我们需要把网站页面变成黑白色或灰色,以表尊敬和纪念。实现网页的置灰效果并不需要将所有的图片和文字都手动换成灰色,这种方法十分繁琐且不现实。它的实现主要是通过filter的grayscale属性,由于滤镜filter会直接影响所有的后代元素,所以只需要给html添加一个统一的灰色滤镜就可以。

html {  filter: grayscale(.95);}

图17 网页置灰效果图


5.2 集卡明暗效果

我们在集卡活动中经常会看到,未点亮的卡片是暗色显示,只有获得了此卡才会高亮显示。这种明暗效果的实现并不用区分点亮和未点亮的卡片进行显示,这其实就可以使用filter的brightness属性进行实现,默认情况下降低亮度,只有点亮了的卡片才恢复亮度。

.unactive {  filter:  brightness(0.5);} .active {  filter: brightness(1);}

图18 集卡明暗效果图


5.3 图像阴影效果

大多数情况下设计提供给我们的图片都是直接使用,如果需要加上阴影的话会默认在图片上进行处理。那么如果是配置化的图片需要统一加上阴影效果呢,开发侧能不能进行统一处理?能的话又要怎么处理呢?

打个比方我们需要给下面的卡券图片再加个阴影效果:

图19 卡券图片

设置阴影的方式我们脑子里肯定首先就会想到box-shadow属性,我们先来看看它的的使用方式。

box-shadow: offset-x offset-y  blur spread color inset;

从box-shadow接受的参数可以看出,drop-shadow可接受的参数和box-shadow属性基本是一样的,但不包含扩张半径spread和inset内阴影等关键字。那他们实现的阴影效果是不是一样的呢?我们先用box-shadow来实现看看。

box-shadow: 2px 4px 3px rgba(50, 50, 0, 0.5);


图20 卡券图片box-shadow效果图

如上图阴影是加了,但是好像哪里不太对,为什么直接给图片外围增加了一个阴影,并没有在卡券本身添加阴影呢?

这是因为在给透明图片添加阴影效果时无法穿透元素,只能添加到透明图片元素的盒模型上,所以最终呈现效果如上图所示。下面我们来使用filter的drop-shadow属性看看。

filter: drop-shadow(2px 4px 3px rgba(50, 50, 0, 0.5));

图21 卡券图片drop-shadow效果图

如上图才是我们期望的效果,阴影效果只作用到卡券本身,这说明drop-shadow的阴影效果是可以穿透透明元素的。除此之外,通过滤镜一些浏览器为了更好的性能会提供硬件加速。但是在使用drop-shadow的投影时,需要注意的是任何非透明的部分都会被打上投影,包括文本(如果背景是透明的)。

图22 drop-shadow效果对比图


5.4 毛玻璃效果

毛玻璃效果是一种常见的视觉效果,它模拟了玻璃表面上由于光线折射而产生的模糊和透明感。我们经常需要使用毛玻璃效果处理内容模糊,以此达到一个信息保护的效果,或者进行背景模糊突出主要内容,引导用户进行操作,在日常开发中其实就是使用了filter的blur属性进行实现的。比如说我们需要给一些产品卡片设置毛玻璃效果:

图23 毛玻璃效果范围超出对比图

使用filter添加模糊后,实际看到的大小会超出我们设置的宽高,这是由于周围的毛边效果产生的,我们可以通过外层设置 overflow: hidden进行处理。不过其实使用filter实现的毛玻璃效果并不是唯一且最好的方式,我们也可以用backdrop-filter属性去实现漂亮的毛玻璃效果,我们来看看他们的实现效果有什么区别。

图24 毛玻璃效果对比图

从上图可以看出,filter明显是对图片本身进行了一个模糊处理,而dropback-filter却像是有一个模糊的遮罩层在上面,我们来看看具体的元素结构。

图25 毛玻璃效果元素结构对比图

由元素结构可以看出,使用backdrop-filter的效果确实比使用filter的效果多了一层置于图片上层的元素,blur效果也并不是作用于图片本身,而是作用于图片的上层元素。

没错,backdrop-filter并不是通过处理图片本身来实现毛玻璃的效果,而是通过在图片上面盖上一个模糊层从而达到毛玻璃的效果,因此要做模糊处理的图片必须置于使用backdrop-filter元素的下面。

那么这个backdrop-filter又是什么呢,与filter又有什么关系或者有区别呢?



6. backdrop-filter

backdrop-filter可以给元素后面区域添加图形效果(如模糊或颜色偏移),而filter属性是将模糊或颜色偏移等图形效果应用于元素。虽然backdrop-filter使用方式同filter属性一致,但它适用于元素背后的所有元素,不过为了看到效果,必须使元素或其背景至少部分透明。

既然backdrop-filter和filter的语法相同,那所支持的滤镜效果和呈现效果会不会也一样呢?

属性值

filter

drop-filter

说明

blur

支持

支持

高斯模糊


brightness

支持

支持

亮度

contrast

支持

支持

对比度

drop-shadow

支持

支持

阴影

grayscale

支持

支持

灰度

hue-rotate

支持

支持

色相

invert

支持

支持

反相

opacity

支持

支持

透明度

sepia

支持

支持

饱和度

saturate

支持

支持

褐色

url

支持

支持

SVG过滤器的URL

由上可知,filter和backdrop-filter支持的滤镜是一模一样的。不过,与filter 不同的就是backdrop-filter是应用于元素后面的内容而不是元素本身,而filter却是作用于元素本身,所以呈现出来的滤镜效果当然是有些不同的。为了更好地看出作用元素的区别,我们再举个简单的例子,依然是模糊效果。

<div class="main">  <div class="child">none</div>  <div class="child filter">filter</div>  <div class="child backdrop-filter">backdrop-filter</div></div>
.main {  width:100vw;  height:100vh;  display: flex;  align-items: center;  justify-content: center;  background: url("xxx.png");  background-size: cover;}.child {  width: 400px;  height:300px;  color: #fff;  font-size: 20px;  text-align: center;  line-height: 300px;  background: rgba(0, 0, 0, .7);  margin-left: 20px;}.filter {  filter: blur(8px);}.backdrop-filter {  backdrop-filter: blur(8px);}

图26 滤镜作用元素效果对比图

由上图我们可以看到filter属性确实只将作用元素本身进行了模糊处理,包括元素的背景和文本内容,而backdrop-filter作用的元素本身却没有任何效果,但是它背后的背景图片部分确实被进行了模糊处理,其实在出现backdrop-filter属性之前,是很难实现给元素背后特定的区域添加滤镜效果的。

那既然滤镜的这两个属性都这么厉害为什么还是不怎么常见,难道是因为兼容性不太好吗?



7. 兼容性

图27 filter兼容性说明图

filter属性在现代浏览器中有很好的支持,包括Chrome、Firefox、Safari、Edge等主流浏览器,部分版本仍需要添加前缀进行兼容性处理。

图28 dropback-filter兼容性说明图

dropback-filter属性的兼容性相对较低,仅在一些较新版本的浏览器中得到支持,且安卓移动端支持较弱。

由上可知,filter属性在大部分浏览器中具有良好的兼容性,但只能调整元素本身的效果;backdrop-filter属性适用于调整元素后面内容的效果,但兼容性相对较差。所以还是需要根据具体的需求和是否需要广泛支持的场景,选择适合的属性去进行对应滤镜效果的处理。

需要注意的是:作用了filter和backdrop-filter的元素会使内部的fixed定位失效,原因是filter值不为none的元素和backdrop-filter值不为none的元素,都会生成新的堆叠上下文。我们都知道css中position: fixed是相对于屏幕视口进行定位的,而作用了filter和backdrop-filter的元素会使得其内部的position: fixed元素不再相对于屏幕视口进行定位,而是相对这个父元素进行定位。



8. 最后

CSS滤镜技术是一个强大而有趣的工具,可以在不依赖图像编辑软件的情况下,在网页设计中应用不同的滤镜效果,也可以为元素增添生动、丰富的视觉效果。

在本文中,我们先介绍了各种滤镜函数的基础用法和显示效果,了解了它们如何改变元素的外观和视觉表现。随后,我们了解了如何通过灵活地应用不同的滤镜函数和组合,去创造出更复杂、个性化的滤镜效果,去实现图像处理和动画,为网页设计增添了更多创意和动感。我们还知道了通过使用filter属性,可以简单地应用模糊(blur)、对比度(contrast)和亮度(brightness)调整、颜色调整等效果,让网页元素瞬间焕发生机。而backdrop-filter属性则允许我们对元素背景内容进行处理,为网页带来更深度的视觉层次。

但是,在使用滤镜效果时我们也要考虑兼容性和性能问题,尽量避免过度使用滤镜造成页面加载缓慢。虽然大多数现代浏览器都支持CSS滤镜,但一些高级滤镜在较旧的浏览器或移动设备上可能会有兼容性问题。因此,我们在使用滤镜时需要进行兼容性测试,并在必要时提供备用方案,以确保在不同平台和设备上都能展现良好的效果。


本文作者


柯琦,来自缦图互联网中心前端团队。

来源-微信公众号:缦图coder

出处
:https://mp.weixin.qq.com/s/x15DGBjOyL8EWtt78xJESg