SVG 滤镜从入门到放弃

想写一篇关于 SVG 滤镜的文章已久,SVG 滤镜的存在,让本来就非常强大的 CSS 如虎添翼。让仅仅使用 CSS/HTML/SVG 创作的效果更上一层楼。题图为袁川老师使用 SVG 滤镜实现的云彩效果 -- CodePen Demo -- Cloud (SVG filter + CSS)

什么是 SVG 滤镜

SVG 滤镜与 CSS 滤镜类似,是 SVG 中用于创建复杂效果的一种机制。很多人看到 SVG 滤镜复杂的语法容易心生退意。本文力图使用最简洁明了的方式让大家尽量弄懂 SVG 滤镜的使用方式。

本文默认读者已经掌握了一定 SVG 的基本概念和用法。

SVG 滤镜的种类

SVG 滤镜包括了:

feBlend
feColorMatrix
feComponentTransfer
feComposite
feConvolveMatrix
feDiffuseLighting
feDisplacementMap
feFlood
feGaussianBlur
feImage
feMerge
feMorphology
feOffset
feSpecularLighting
feTile
feTurbulence
feDistantLight
fePointLight
feSpotLight

看着内容很多,有点类似于 CSS 滤镜中的不同功能:blur()contrast()drop-shadow()

SVG 滤镜的语法

我们需要使用 标签来定义一个 SVG 滤镜。

通常所有的 SVG 滤镜元素都需要定义在 标记内。

现在,基本上现代浏览器,即使不使用 包裹 ,也能够定义一个 SVG 滤镜。

这个 标记是 definitions 这个单词的缩写,可以包含很多种其它标签,包括各种滤镜。

其次,使用 标记用来定义 SVG 滤镜。 标签需要一个 id 属性,它是这个滤镜的标志。SVG 图形使用这个 id 来引用滤镜。

看一个简单的 DEMO:

div {
    width: 100px;
    height: 100px;
    background: #000;
}
.cssblur {
    filter: blur(5px);
}
.svgFilter{
    filter: url(#blur);
}

这里,我们在 defsfilter 标签内,运用了 SVG 的 feGaussianBlur 滤镜,也就是模糊滤镜, 该滤镜有两个属性 instdDeviation。其中 in="SourceGraphic" 属性指明了模糊效果要应用于整个图片,stdDeviation 属性定义了模糊的程度。最后,在 CSS 中,使用了 filter: url(#blur) 去调用 HTML 中定义的 id 为 blur 的滤镜。

为了方便理解,也使用 CSS 滤镜 filter: blur(5px) 实现了一个类似的滤镜,方便比较,结果图如下:

SVG 滤镜从入门到放弃_第1张图片

CodePen Demo - SVG 滤镜

嘿,可以看到,使用 SVG 的模糊滤镜,实现了一个和 CSS 模糊滤镜一样的效果。

CSS filter 的 url 模式

上文的例子中使用了 filter: url(#blur) 这种模式引入了一个 SVG 滤镜效果,url 是 CSS 滤镜属性的关键字之一,url 模式是 CSS 滤镜提供的能力之一,允许我们引入特定的 SVG 过滤器,这极大的增强 CSS 中滤镜的能力。

相当于所有通过 SVG 实现的滤镜效果,都可以快速的通过 CSS 滤镜 URL 模式一键引入。

多个滤镜搭配工作

和 CSS 滤镜一样,SVG 滤镜也是支持多个滤镜搭配混合使用的。

所以我们经常能看到一个 标签内有大量的代码。很容易就懵了~

再来看个简单的例子:

div {
    width: 200px;
    height: 200px;
    background: url(xxx);
    filter: url(#MyFilter);
}

我们先来看看整个滤镜的最终结果,结果长这样:

SVG 滤镜从入门到放弃_第2张图片

CSS 可能一行代码就能实现的事情,SVG 居然用了这么多代码。(当然,这里 CSS 也不好实现,不是简单容器的阴影,而是 PNG 图片图形的轮廓阴影)

分解步骤

首先看这一段:



首先 这一段,我们上面也讲到了,会生成一个模糊效果,这里多了一个新的属性 result='blur',这个就是 SVG 的一个特性,不同滤镜作用的效果可以通过 result 产出一个中间结果(也称为 primitives 图元),其他滤镜可以使用 in 属性导入不同滤镜产出的 result,继续操作。

紧接着, 滤镜还是很好理解的,使用 in 拿到了上一步的结果 result = 'blur',然后做了一个简单的位移。

这里就有一个非常重要的知识点:在不同滤镜中利用 resultin 属性,可以实现在前一个基本变换操作上建立另一个操作,比如我们的例子中就是添加模糊后又添加位移效果。

结合两个滤镜,产生的图形效果,其实是这样的:

SVG 滤镜从入门到放弃_第3张图片

实际效果中还出现了原图,所以这里我们还使用了 标签,合并了多个效果。也就是上述这段代码:



    
    

feMerge 滤镜允许同时应用滤镜效果而不是按顺序应用滤镜效果。利用 result 存储别的滤镜的输出可以实现这一点,然后在一个 子元素中访问它。

  • 表示了上述两个滤镜的最终输出结果 offsetBlur ,也就是阴影的部分
  • 中的 in="SourceGraphic" 关键词表示图形元素自身将作为 原语的原始输入

整体再遵循后输入的层级越高的原则,最终得到上述结果。示意流程图如下:

SVG 滤镜从入门到放弃_第4张图片

至此,基本就掌握了 SVG 滤镜的工作原理,及多个滤镜如何搭配使用。接下来,只需要搞懂不同的滤镜能产生什么样的效果,有什么不同的属性,就能大致对 SVG 滤镜有个基本的掌握!

关于 SVG 滤镜还需要知道的

上面大致过了一下 SVG 滤镜的使用流程,过程中提到了一些属性,可能也漏掉了一些属性的讲解,本章节将补充说明一下。

滤镜标签通用属性

有一些属性是每一个滤镜标签都有,都可以进行设置的。

属性 作用
x, y 提供左上角的坐标来定义在哪里渲染滤镜效果。 (默认值:0)
width, height 绘制滤镜容器框的高宽(默认都为 100%)
result 用于定义一个滤镜效果的输出名字,以便将其用作另一个滤镜效果的输入(in)
in 指定滤镜效果的输入源,可以是某个滤镜导出的 result,也可以是下面 6 个值

in 属性的 6 个取值

SVG filter 中的 in 属性,指定滤镜效果的输入源,可以是某个滤镜导出的 result,也可以是下面 6 个值:

in 取值 作用
SourceGraphic 该关键词表示图形元素自身将作为 原语的原始输入
SourceAlpha 该关键词表示图形元素自身将作为 原语的原始输入。SourceAlphaSourceGraphic 具有相同的规则除了 SourceAlpha 只使用元素的非透明部分
BackgroundImage
BackgroundAlpha
FillPaint
StrokePaint

更多 SVG 滤镜介绍讲解

上面已经提到了几个滤镜,我们简单回顾下:

  • - 模糊滤镜
  • - 位移滤镜
  • - 多滤镜叠加滤镜

接下来再介绍一些比较常见,有意思的 SVG 滤镜。

feBlend 滤镜

为混合模式滤镜,与 CSS 中的混合模式相类似。

在 CSS 中,我们有混合模式 mix-blend-modebackground-blend-mode 。我有过非常多篇关于 CSS 混合模式相关的一些应用。如果你还不太了解 CSS 中的混合模式,可以先看看这几篇文章:

SVG 中的混合模式种类比 CSS 中的要少一些,只有 5 个,其作用与 CSS 混合模式完全一致:

  • normal — 正常
  • multiply — 正片叠底
  • screen — 滤色
  • darken — 变暗
  • lighten— 变亮

简单一个 Demo,我们有两张图,利用不同的混合模式,可以得到不一样的混合结果 :

.container {
    width: 200px;
    height: 250px;
    filter: url(#lighten);
}

这里还用到了一个 滤镜,它的作用是提供像素数据作为输出,如果外部来源是一个 SVG 图像,这个图像将被栅格化。

SVG 滤镜从入门到放弃_第5张图片

上述运用了 feBlend 滤镜中的 mode="lighten" 后的结果,两个图像叠加作用了 lighten 混合模式:

SVG 滤镜从入门到放弃_第6张图片

看看全部 5 中混合模式的效果:

SVG 滤镜从入门到放弃_第7张图片

CodePen Demo -- SVG Filter feBlend Demo

feColorMatrix

滤镜也是 SVG 滤镜中非常有意思的一个滤镜,顾名思义,它的名字中包含了矩阵这个单词,表示该滤镜基于转换矩阵对颜色进行变换。每一像素的颜色值(一个表示为[红,绿,蓝,透明度] 的矢量) 都经过矩阵乘法 (matrix multiplated) 计算出的新颜色。

这个滤镜稍微有点复杂,我们一步一步来看。

滤镜有 2 个私有属性 typevalues,type 它支持 4 种不同的类型:saturate | hueRotate | luminanceToAlpha | matrix,其中部分与 CSS Filter 中的一些滤镜效果类似。

type 类型 作用 values 的取值范围
saturate 转换图像饱和度 0.0 - 1.0
hueRotate 转换图像色相 0.0 - 360
luminanceToAlpha 阿尔法通道亮度(不知道如何翻译 :sad) 只有一个效果,无需改变 values 的值
matrix 使用矩阵函数进行色彩变换 需要应用一个 4 x 5 的矩阵

在这里,我做了一个简单的关于 前 3 个属性 saturate | hueRotate | luminanceToAlpha 的效果示意 DEMO -- CodePen - feColorMatrix Demo,可以感受下它们的具体的效果:

SVG 滤镜从入门到放弃_第8张图片

saturate、hueRotate 滤镜和 CSS 中的 filter 中的 saturate、hue-rotate 的作用是一模一样的。

feColorMatrix 中的 type=matrix

feColorMatrix 中的 type=matrix 理解起来要稍微更复杂点,它的 values 需要传入一个 4x5 的矩阵。

像是这样:


  

要理解如何运用这些填写矩阵,就不得不直面另外一个问题 -- 图像的表示。

数字图像的本质是一个多维矩阵。在图像显示时,我们把图像的 R 分量放进红色通道里,B 分量放进蓝色通道里,G 分量放进绿色通道里。经过一系列处理,显示在屏幕上的就是我们所看到的彩色图像了。

而 feColorMatrix 中的 matrix 矩阵,就是用来表示不同通道的值每一个分量的值,最终通过计算得到我们熟知的 rgba() 值。

计算逻辑为:

/* R G B A 1 */ 
1 0 0 0 0 // R = 1*R + 0*G + 0*B + 0*A + 0 
0 1 0 0 0 // G = 0*R + 1*G + 0*B + 0*A + 0 
0 0 1 0 0 // B = 0*R + 0*G + 1*B + 0*A + 0 
0 0 0 1 0 // A = 0*R + 0*G + 0*B + 1*A + 0

中文的文章,对 feColorMatrix 的 matrix 讲解最好的应该就是大漠老师的这篇 -- 详解feColorMatrix,对具体的表示法感兴趣的可以看看。

仅仅是使用的话,这里还有一个可视化的 DEMO -- CodePen - feColorMatrix Demo,帮助大家理解记忆:

SVG 滤镜从入门到放弃_第9张图片


到目前为止,大部分 SVG 滤镜的展示讲解都是 CSS 现有能力能够实现的,那 SVG 滤镜的独特与魅力到底在哪呢?有什么是 CSS 能力无法做到的么?下面来看看另外几个有意思的 SVG 滤镜。

feSpecularLighting/feDiffuseLighting 光照滤镜

feSpecularLighting 与 feDiffuseLighting 都意为光照滤镜,使用它们可以照亮一个源图形,不同的是,feSpecularLighting 为镜面照明,而 feDiffuseLighting 为散射光照明。

  • feDiffuseLighting:来自外部光源,适合模拟太阳光或者灯光照明
  • feSpecularLighting:指定从反射面反射的二次光

简单看其中一个 Demo,代码看着有点多,但是一步一步也很好理解:

div {
    background: url(avator.png);
}
.svg-filter {
    filter: url(#filter);
}

左边是原图,右边是应用了光照滤镜之后的效果。

SVG 滤镜从入门到放弃_第10张图片

CodePen - feSpotLight SVG Light Source

feMorphology 滤镜

feMorphology 为形态滤镜,它的输入源通常是图形的 alpha 通道,用来它的两个操作可以使源图形腐蚀(变薄)或扩张(加粗)。

使用属性 operator 确定是要腐蚀效果还是扩张效果。使用属性 radius 表示效果的程度,可以理解为笔触的大小。

  • operator:erode 腐蚀模式,dilate 为扩张模式,默认为 erode
  • radius:笔触的大小,接受一个数字,表示该模式下的效果程度,默认为 0

我们将这个滤镜简单的应用到文字上看看效果:

Normal Text

Normal Text

Normal Text

p {
    font-size: 64px;
}
.dilate {
    filter: url(#dilate);
}
.erode {
    filter: url(#erode);
}

效果如下:最左边的是正常文字,中间的是扩张的模式,右边的是腐蚀模式,看看效果,非常好理解:

image

当然,我们还可以将其运用在图片之上,这时,并非是简单的让图像的笔触变粗或者变细,

  • 对于 erode 模式,会将图片的每一个像素向更暗更透明的方向变化,
  • dilate 模式,则是将每个向像素周围附近更亮更不透明的方向变化

简单看个示例动画 DEMO,我们有两张图,分别作用 operator="erode"operator="dilate",并且动态的去改变它们的 radius,其中一个的代码示意如下:


    
        
            
        
    

    

SVG 滤镜从入门到放弃_第11张图片

上图左边是扩张模式,右边是腐蚀模式:

CodePen Demo -- SVG feMorphology Animation

feTurbulence 滤镜

turbulence 意为湍流,不稳定气流,而 SVG 滤镜能够实现半透明的烟熏或波状图像。 通常用于实现一些特殊的纹理。滤镜利用 Perlin 噪声函数创建了一个图像。噪声在模拟云雾效果时非常有用,能产生非常复杂的质感,利用它可以实现了人造纹理比如说云纹、大理石纹的合成。

有了 feTurbulence,我们可以自使用 SVG 创建纹理图形作为置换图,而不需要借助外部图形的纹理效果,即可创建复杂的图形效果。

这个滤镜,我个人认为是 SVG 滤镜中最有意思的一个,因为它允许我们自己去创造出一些纹理,并且叠加在其他效果之上,生成出非常有意思的动效。

feTurbulence 有三个属性是我们特别需要注意的:typebaseFrequencynumOctaves

  • type:实现的滤镜的类型,可选fractalNoise 分形噪声,或者是 turbulence 湍流噪声。

    • fractalNoise:分形噪声更加的平滑,它产生的噪声质感更接近云雾
    • turbulence:湍流噪声
  • baseFrequency: 表示噪声函数的基本频率的参数,频率越小,产生的图形越大,频率越大,产生的噪声越复杂其图形也越小越精细,通常的取值范围在 0.02 ~ 0.2
  • numOctaves:表示噪声函数的精细度,数值越高,产生的噪声更详细。 默认值为1

这里有一个非常好的网站,用于示意 feTurbulence 所产生的两种噪声的效果:http://apike.ca/ - feTurbulence

两种噪声的代码基本一致,只是 type 类型不同:


  


  

我们通过改变 baseFrequencynumOctaves 参数看看实际产生的两种噪声的效果:

同时,baseFrequency 允许我们传入两个值,我们可以只改变某一方向上的频率,具体的你可以戳这个 Demo 看看:CodePen -- feTurbulence baseFrequency & numOctaves

单单一个 滤镜其实是比较难搞懂这滤镜想干什么的,需要将这个滤镜作为纹理或者输入,和其他滤镜一起搭配使用,实现一些效果,下面我们来看看:

使用 feTurbulence 滤镜实现文字流动的效果

首先,尝试将 feTurbulence 所产生的纹理和文字相结合。

简单的代码如下:

Coco
Coco
.turbulence {
    filter: url(#fractal);
}

左边是正常的效果,后边是应用了 的效果,你可以试着点进 Demo,更改 baseFrequencynumOctaves 参数的大小,可以看到不同的效果:

SVG 滤镜从入门到放弃_第12张图片

CodePen Demo -- feTurbulence text demo

feDisplacementMap 映射置换滤镜

上面的 Demo 还用到了 feDisplacementMap 滤镜,也需要简单的讲解下。

feDisplacementMap 为映射置换滤镜,想要用好这个滤镜不太容易,需要掌握非常多的关于 PhotoShop 纹理创建或者是图形色彩相关的知识。该滤镜用来自图像中从 in2 的输入值到空间的像素值置换图像从 in 输入值到空间的像素值。

说人话就是 feDisplacementMap 实际上是用于改变元素和图形的像素位置的。该滤镜通过遍历原图形的所有像素点,使用 feDisplacementMap 重新映射到一个新的位置,形成一个新的图形。

在上述的 feTurbulence 滤镜与文字的结合使用中,我们通过 feTurbulence 噪声得到了噪声图形,然后通过 feDisplacementMap 滤镜根据 feTurbulence 所产生的噪声图形进行形变,扭曲,液化,得到最终的效果。

MDN 上有这个滤镜转化的一个公式(感兴趣的可以研究下,我啃不动了):

P'(x,y) ← P( x + scale * (XC(x,y) - 0.5), y + scale * (YC(x,y) - 0.5))

使用 feTurbulence 滤镜实现褶皱纸张的纹理

好,我们继续 feTurbulence ,使用这个滤镜,我们可以生成各种不同的纹理,我们可以尝试使用 feTurbulence 滤镜搭配光照滤镜实现褶皱的纸张纹理效果,代码也非常少:

div {
    width: 650px;
    height: 500px;
    filter: url(#roughpaper);
}

效果如下:

SVG 滤镜从入门到放弃_第13张图片

CodePen Demo -- Rough Paper Texture with SVG Filters

你可以在 Sara Soueidan 的一次关于 SVG Filter 的分享上,找到制作它的教程:Youtube -- SVG Filters Crash Course

使用 feTurbulence 滤镜实现按钮hover效果

使用 feTurbulence 滤镜搭配 feDisplacementMap 滤镜,还可以制作一些非常有意思的按钮效果。

尝试实现一些故障风格的按钮,其中一个按钮的代码如下:

Button
Button
.fe1 {
    width: 200px;
    height: 64px;
    outline: 200px solid transparent;
}

.fe1:hover {
    filter: url(#fe1);
}

通过 hover 按钮的时候,给按钮添加滤镜效果,并且滤镜本身带有一个无限循环的动画:

完整的代码你可以戳这里:CodePen Demo - SVG Filter Button Effects

使用 feTurbulence 滤镜实现云彩效果

最后,我们回到题图上的云彩效果,使用 feTurbulence 滤镜,我们可以非常逼真的使用 SVG 模拟出真实的云彩效果。

首先,通过随机生成的多重 box-shadow,实现这一一个图形:

div {
    width: 1px;
    height: 1px;
    box-shadow: rgb(240 255 243) 80vw 11vh 34vmin 16vmin, rgb(17 203 215) 33vw 71vh 23vmin 1vmin, rgb(250 70 89) 4vw 85vh 21vmin 9vmin, rgb(198 241 231) 8vw 4vh 22vmin 12vmin, rgb(198 241 231) 89vw 11vh 31vmin 19vmin, rgb(240 255 243) 5vw 22vh 38vmin 19vmin, rgb(250 70 89) 97vw 35vh 33vmin 16vmin, rgb(250 70 89) 51vw 8vh 35vmin 14vmin, rgb(17 203 215) 75vw 57vh 40vmin 4vmin, rgb(250 70 89) 28vw 18vh 31vmin 11vmin, rgb(250 70 89) 8vw 89vh 31vmin 2vmin, rgb(17 203 215) 13vw 8vh 26vmin 19vmin, rgb(240 255 243) 98vw 12vh 35vmin 5vmin, rgb(17 203 215) 35vw 29vh 27vmin 18vmin, rgb(17 203 215) 67vw 58vh 22vmin 15vmin, rgb(198 241 231) 67vw 24vh 25vmin 7vmin, rgb(17 203 215) 76vw 52vh 22vmin 7vmin, rgb(250 70 89) 46vw 86vh 26vmin 20vmin, rgb(240 255 243) 50vw 20vh 25vmin 1vmin, rgb(250 70 89) 74vw 14vh 25vmin 16vmin, rgb(240 255 243) 31vw 100vh 29vmin 20vmin
}

这个工作,你可以交给 SASS、LESS 或者 JavaScript 这些能够有循环函数能力的语言去生成,它的效果大概是这样:

SVG 滤镜从入门到放弃_第14张图片

紧接着,通过 feTurbulence 产生分形噪声图形,使用 feDisplacementMap 进行映射置换,最后给图形叠加上这个滤镜效果。


  
    
    
  
div {
    filter: url(#filter);
}

即可得到这样的云彩效果:

SVG 滤镜从入门到放弃_第15张图片

完整的代码,你可以戳这里到袁川老师的 CodePen 观看:Cloud (SVG filter + CSS)

总结一下

关于 SVG 滤镜入门的第一篇总算差不多了,本文简单的介绍了一下 SVG 滤镜的使用方式以及一些常见的 SVG 滤镜并给出了最简单的一些使用效果,希望大家看完能对 SVG 滤镜有一个简单的认识。

本文罗列的滤镜效果更多的是单个效果或者很少几个组合在一起的效果,实际的使用或者应用到应用场景下其实会是更多滤镜的的组合产生出的一个效果。

后面的文章将会更加细致的去探讨分析多个 SVG 滤镜组合效果,探讨更复杂的排列组合。

文章的题目叫SVG 滤镜从入门到放弃因为 SVG 滤镜学起来确实太繁琐太累了,它不像 CSS 滤镜或者混合模式那么容易上手那么简单。当然也由于 SVG 滤镜的功能非常强大,定制化能力强以及它已经存在了非常之久有关。SVG 滤镜的兼容性也很好,它们其实是早于 CSS3 一些特殊效果之前就已经存在的。

CSS 其实一直在向 SVG 的一些特殊能力靠拢,用更简单的语法让人更易上手,不过 SVG 滤镜还是有其独特的魅力所在。后续将会有更多关于 SVG 滤镜的文章。也希望读到这里的同学不要放弃!

参考资料

最后

好了,本文到此结束,希望对你有帮助 :)

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 -- iCSS前端趣闻

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

你可能感兴趣的:(css前端css3滤镜svg)