SVG 滤镜
SVG 滤镜是什么
SVG 滤镜是用来为 SVG 中的形状或图形创建复杂效果的一种机制,SVG 提供了以下滤镜可供使用:
<feBlend>
:用于将两个图像按照指定的混合模式进行混合。<feColorMatrix>
:用于通过矩阵操作调整图像的颜色和透明度。<feComponentTransfer>
:用于对图像的 RGBA 通道进行独立的颜色处理和调整。<feComposite>
:用于将多个图像按照指定的合成操作进行合成。<feConvolveMatrix>
:用于应用卷积操作,可以实现模糊、锐化等效果。<feDiffuseLighting>
:用于根据光源的位置和属性计算图像的表面照明效果。<feDisplacementMap>
:用于根据位图图像的像素值来扭曲原始图像,创建位移效果。<feDropShadow>
:用于在图像或文本周围添加投影阴影效果。<feFlood>
:用于创建一个填充整个图像区域的颜色或渐变。<feGaussianBlur>
:用于将图像进行高斯模糊处理。<feImage>
:用于在图像中插入外部的位图图像。<feMerge>
:用于将多个图像按照指定的顺序进行合并。<feMorphology>
:用于对图像进行形态学处理,如膨胀或腐蚀。<feOffset>
:用于将图像进行平移偏移。<feSpecularLighting>
:用于根据光源的位置和属性计算图像的镜面高光效果。<feTile>
:用于将图像平铺到指定的区域内。<feTurbulence>
:用于创建具有噪点或纹理效果的图像。
SVG 滤镜定义在 <filter>
标签内,并且可以使用 id
属性来为其指定一个 ID:
<svg xmlns="http://www.w3.org/2000/svg">
<filter id="filter-name">
<!-- 在这里定义 SVG 滤镜... -->
</filter>
</svg>
例如,这是一个定义了 <feGaussianBlur>
滤镜的 SVG 滤镜组,并且为其指定了 blur
这个 ID。
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="blur">
<feGaussianBlur stdDeviation="18" />
</filter>
</defs>
</svg>
至于为什么这里要特别提到 <filter>
标签支持 id
属性,继续往下阅读你就知道啦~
已经有 CSS 滤镜了,要这 SVG 滤镜有什么用呢
相比于 CSS 滤镜,SVG 滤镜可以实现一些 CSS 滤镜难以实现的效果,例如 CSS 滤镜中的 blur()
只能指定一个参数,用来控制整体的模糊程度,但是不能分别设定在 x 轴和 y 轴上的模糊程度。而 SVG 中的 <feGaussianBlur>
滤镜则可以分别设定在 x 轴和 y 轴上的模糊程度,<feGaussianBlur>
滤镜接受一个 stdDeviation
参数,如果只为这个参数设定单个数值,则和 CSS 中的 blur()
滤镜效果是一样的;如果为这个参数设定两个数值,例如 <feGaussianBlur stdDeviation="10 0" />
,则前面的值表示在 x 轴上的模糊程度,后面的值表示在 y 轴上的模糊程度。效果如下:
See the Pen feGaussianBlur by LGiki (@lgiki-the-bold) on CodePen.
另外,SVG 的滤镜还支持 in
和 result
属性,可以将多个不同滤镜串联起来使用,做出很多 CSS 滤镜难以做出来的效果,并且 in
属性还支持输入不同类型的值,例如设定为 SourceGraphic
表示将图像自身作为滤镜的输入、设定为 SourceAlpha
表示将元素的透明度作为滤镜的输入等…
可以说,SVG 的滤镜就像 Photoshop 的图层混合模式,可以随意搭配组合,构建出各种奇特的效果。
可是…CSS 滤镜可以直接运用于元素上,SVG 滤镜可以吗?
当然可以!前面有提到 SVG 的 <filter>
标签可以设定 id
属性,有了 id 之后只需在 CSS 中为元素增加 filter: url('#filter-name')
就可以将 SVG 滤镜应用于任意 HTML 元素上了,例如这样:
.selector {
filter: url('#filter-name');
/* 另外,也可以从外部 SVG 文件中加载 filter: */
filter: url('svg_file.svg#filter-name');
}
这就是 SVG 滤镜好用的地方了,可以运用在任意的 HTML 元素上,例如网页上的文字、图片、视频等各种元素,可以实现很多奇特、有趣的效果,例如接下来要介绍的 Gooey Effect。
另外,在 https://caniuse.com/svg-filters 和 https://caniuse.com/css-filters 的对比中可以看到,各家浏览器对 SVG 滤镜对支持其实是比 CSS 滤镜更好的。
Gooey Effect
先来看看 Gooey Effect 长什么样子:(请使用 Firefox 或 Chrome 浏览器打开,请勿使用 Safari 浏览器)
See the Pen Gooey Effect by LGiki (@lgiki-the-bold) on CodePen.
我一开始是在 https://css-tricks.com/gooey-effect/ 发现 Gooey Effect 的,第一次看到这个效果的时候觉得很震撼,在看代码之前完全想不懂是如何实现的。看完之后才知道,原来通过 SVG 滤镜就能实现这么有趣的效果,这也是我开始研究 SVG 滤镜并写下这篇文章的原因。
如果想详细了解如何使用 SVG 滤镜实现 Gooey Effect 可以点击原文链接查看,下面我简单介绍一下这个效果的实现。
首先,从代码里面可以看到,这个特效本质上是通过 <feGaussianBlur>
、<feColorMatrix>
和 <feBlend>
这三个滤镜实现的,那么就分别来看看这三个滤镜。
<feGaussianBlur>
滤镜
前文有提到,<feGaussianBlur>
滤镜就是用来实现高斯模糊的滤镜,接受一个 stdDeviation
参数,表示模糊的程度,值越大,模糊的程度越大,反之则越小,并且支持分开设定图形在 x 轴和 y 轴上的模糊程度。高斯模糊效果在很多软件中都有应用,例如 macOS、iOS 在很多 UI 的设计上就大量使用了高斯模糊的元素,Windows 10、Windows 11 中也有一定的运用。
而当在两个互相接近的移动图形上叠加一个高斯模糊滤镜之后,在图形接近/分离的时候,边缘会出现类似于互相吸附的效果:(你可以点击 blur
选框来切换是否模糊)
See the Pen Gooey-Effect-Step-1 by LGiki (@lgiki-the-bold) on CodePen.
这就是 Gooey Effect 的关键,通过高斯模糊制造出两个图形互相吸附的效果。那么,接下来的问题就是,如何保留这种图形互相吸附的效果,同时让图形的边缘清晰。仔细观察这个高斯模糊之后的图形,如果可以把图形边缘模糊的像素映射为黑色似乎就能解决这个问题,于是就可以使用 <feColorMatrix>
滤镜来实现这件事情。
<feColorMatrix>
滤镜
SVG 中的 <feColorMatrix>
滤镜可以基于一个转换矩阵对元素的颜色进行转换,可以对元素的 RGBA 四个通道的颜色进行转换,其中,转换矩阵是一个 4 行 5 列的矩阵,定义如下:
假设用 $R^\prime$、$G^\prime$、$B^\prime$、$A^\prime$ 分别表示经过 <feColorMatrix>
滤镜后每个像素点的 RGBA 值,那么 <feColorMatrix>
的运算实际上就是一个矩阵乘法的过程:
假设 <feColorMatrix>
的变换矩阵为:
那么实际上做的运算就是:
本质上就是把原本蓝色通道的值调整为原来的 2 倍,其余通道保持不变,那么图片应该就会变得更显蓝色一些。
我写了一个 feColorMatrix 的 Playground,你可以在线调节转换矩阵的值来看看对图片颜色的影响:https://fecolormatrix-playground.vercel.app/
好了,回到 Gooey Effect,现在要做的事就是把图形边缘变得清晰,把模糊部分的像素映射为黑色即可。
那些模糊的像素,其实就是 Alpha 通道,因此,可以通过 <feColorMatrix>
滤镜把 Alpha 通道的像素映射为
首先需要了解一下 Alpha 通道,假设背景色为白色(#ffffff
)、前景色为黑色(#000000
),那么在不同 Alpha 值下的颜色如下所示,从左到右分别是 Alpha 为 0 到 Alpha 为 255 的颜色:
从上文经过 <feGaussianBlur>
滤镜之后得到的图形来看,边缘模糊的像素本质上是透明度(Alpha 值)各不相同的像素,因此如果我们能把边缘那些半透明的像素变为纯色的像素,不就可以让图形的边缘清晰起来,同时又具有吸附效果了吗!这时候就可以通过 <feColorMatrix>
滤镜实现。
在原文中给出的 <feColorMatrix>
滤镜的参数是:
即将 Alpha 通道扩大到原来的 18 倍再减去 7:
这里有一个简单的示例程序,你可以拖动顶部的拖动条来控制 Alpha to Alpha
(即 feColorMatrix
的第 4 行第 4 列,默认为 18)和 Alpha Offset
(即 feColorMatrix
的第 4 行第 5 列,默认为 -7)的值:
See the Pen Gooey Effect with Controller by LGiki (@lgiki-the-bold) on CodePen.
你可以试着先将 Alpha Offset
的值调整为 0,然后拖动 Alpha to Alpha
看看随着 Alpha 通道的系数变大或变小,图像会出现什么样的变化,等固定在某个值之后,再试着拖动 Alpha Offset
看看图像又会出现什么变化,就能理解这个过程了。
<feBlend>
滤镜
<feBlend>
滤镜顾名思义就是将两个元素按照特定的混合模式混合,支持三个参数:in
、in2
和 mode
,其中,in
和 in2
就是输入的两个图形,mode
即对这两个图形的混合模式,mode
的默认值是 normal
,即这两张图正常叠加,in
显示在 in2
的顶层。
References
- https://developer.mozilla.org/en-US/docs/Web/SVG/Element/filter
- https://caniuse.com/svg-filters
- https://caniuse.com/css-filters
- https://css-tricks.com/gooey-effect/
- https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feColorMatrix
- https://en.wikipedia.org/wiki/Matrix_multiplication
- https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feGaussianBlur
- https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feBlend