本文主要为 SVG 过滤器的人话解释和实践,不会有任何未知知识造成的心理不适,通过阅读可以学会 SVG 过滤器的常用操作。
SVG 过滤器基础知识
SVG 意为可缩放矢量图形,通过 XML 格式定义图像,在 HTML 开发中有广泛的使用。其中 SVG 过滤器可以用来给图形添加一些特殊效果,例如模糊、阴影、色彩转换等等。
滤镜效果将图形操作(例如模糊、照明、颜色转换和扭曲)应用于内容。现在流行的过滤器就两种:
-
CSS 过滤器:CSS 的属性 filter 将模糊或颜色偏移等图形效果应用于元素。滤镜通常用于调整图像、背景和边框的渲染。可应用于任何 HTML 内容。
-
SVG 过滤器:可应用于 SVG 内容(以及通过 CSS 过滤器交叉引用的 HTML 内容)的图形效果组合。
SVG 过滤器的扩展性相比 CSS 过滤器要多一些选择。
为什么要使用 SVG 过滤器
运行时效果动画化,可以防止大型图像下载对性能的影响(例如噪声纹理)。与 Canvas 和 CSS 效果相比,SVG 过滤器的优点在于:
-
无需下载大图像即可将纹理添加到 SVG 图形。
-
Web 应用程序中嵌入客户端图像编辑功能。
-
为文本添加光照、浮雕和变形效果,无需将文本转换为图像,保持可访问性和可搜索性。
基础布局和基本参数
defs
: SVG 允许我们定义以后需要重复使用的图形元素。 可以把所有需要再次使用的引用元素都放在defs
元素里。这样做可以增加 SVG 内容的易读性和可访问性。filter
:filter
元素作用是作为滤镜操作的容器。过滤器需要有一个id
属性,它不能直接呈现。可以利用目标 SVG 元素上的 filter 属性引用一个滤镜。
<svg>
<defs>
<filter id="filter">
<!-- fe 的各种元素放在这里-->
</filter>
</defs>
<text class="filtered" x="20" y="140">SVG FONT DEMO</text>
</svg>
复制代码
那基础框架写完了, 怎么让滤镜和我们的文本做关联呢,我们可以通过 CSS 将文本和 filter
做绑定,这样过滤器的效果就会复用在我们的文本元素中了。
不过这里要多说一嘴,SVG 过滤可用于创建图像特殊效果。过滤器处理光栅图像而不是矢量图形。因此,本文中的所有内容都讨论像素和图像,而不是矢量!
.filtered {
filter: url(#filter);
}
复制代码
然后我们要真正实现过滤器,我们就要在 filter
添加具体的滤镜,常见的滤镜有以下几种
字段 | 解释 |
---|---|
feMerge | 合并滤镜,可以使 SVG 同时使用多个滤镜效果 |
feMorphology | 主要用来扩张输入的图像,使图像加粗或变瘦 |
feComposite | 组合两个输入的图像,可以对图像取交集或者并集 |
feTurbulence | 给图像增加纹理 |
feDisplacementMap | 对图像做映射 |
feColorMatrix | 对颜色做矩阵变换,其实就是输出一个颜色 |
feOffset | 偏移图像位置 |
feFlood | 填充图像 |
feBlend | 组合两个图像到一个图层 |
feGaussianBlur | 高斯模糊图像 |
feImage | 可以使用JPG、PNG、SVG文件或SVG元素作输入源 |
举一个很简单的例子,我们想用 SVG 过滤器实现一个 div 的模糊处理,我们可以通过如下代码来实现
一般过滤器模块都会有一个 in
参数和一个 result
参数作为输入输出,如果我们想要使用原始图像我们只需要 in="SourceGraphic"
载入原始图形即可。
<div class="box" style="filter: url(#filter)"></div>
<svg>
<filter id="filter">
<feGaussianBlur in="SourceGraphic" stdDeviation="3"></feGaussianBlur>
</filter>
</svg>
复制代码
是不是感觉很简单,现在我们动手实践一个 SVG 过滤器模块的案例
使用 SVG 过滤器对字体做特效
首先先来看一下我们要做的结果,这个效果完全就是基于过滤器实现的,接下来我们就来一步一步实现这个字体并介绍过滤器的具体使用方法
step1: 完成基础过滤器配置
我们先来写一个框架,定义一个需要改造的字体文案
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<defs>
<filter id="filter">
<!-- 待填充 -->
</filter>
</defs>
<text class="filtered" x="20" y="140">SVG FONT DEMO</text>
</svg>
复制代码
svg {
display: block;
position: relative;
width: 1000px;
height: 200px;
top: 50%;
transform: translateY(-50%);
margin: 0 auto;
overflow: hidden;
}
.filtered {
filter: url(#filter);
fill: #9673ff;
color: #9673ff;
font-weight: 900;
font-size: 90px;
}
复制代码
到这里我们还是什么都看不到,因为我们一个滤镜都没添加,现在我们来往里一个个添加
step2: 绘制边框
feMorphology 滤镜
使用 feMorphology 先来改变字体粗细,这里定义 feMorphology 的输出值为 OUTLINE_10
方便后续使用,具体的值通过 radius
设置,radius
有两个值。
erode
定义的源图形变细dilate
定义的源图形变胖
<filter>
<feMorphology
operator="dilate"
radius="5"
in="SourceAlpha"
result="OUTLINE_10"
/>
<!-- 待填充 -->
</filter>
复制代码
feComposite 滤镜
这个滤镜的 operator
字段比较麻烦,它可以让两个输入图像逐像素在图像空间形成组合组合,feComposite 有 over
、in
、 atop
、out
、xor、 lighter
、 arithmetic
这些参数,这里我们使用 out
字段,out
字段定义图像显示的是由 in
属性定义的源图形中,位于 in2
的目标图形之外的部分。
<filter>
<feComposite
operator="out"
in="OUTLINE_10"
in2="SourceAlpha"
result="OUTLINE_20"
/>
<!-- 待填充 -->
</filter>
复制代码
feTurbulence 滤镜
这个滤镜就很秀了,feTurbulence 滤镜也叫作湍流滤镜。在自然界中,很多物体,很多材质的纹理都表现为不规则,而我们代码实现的一般都是有规律的扁平效果,feTurbulence 就可以实现这种随机效果,他的参数可能第一眼看起来也有些麻烦,不过这里会具体介绍一下
- baseFrequency : 滤波器基元噪声函数的基频参数,改变它的值可以改变噪声的密集程度
- type: 支持
fractalNoise
和turbulence
, 指示过滤器原语是否应该执行噪声或湍流功能,具体可以戳这里看一下实际效果。 - numOctaves:定义噪声的倍频,倍频的数量越多,噪声看起来越自然
- seed:他可以改变噪声的形状
<feTurbulence
baseFrequency=".05"
type="fractalNoise"
numOctaves="3"
seed="0"
result="Texture_10"
/>
复制代码
如上我们就生成了一段这样的噪声
那么噪声该如何应用到过滤器中呢,就需要用到接下来的 feDisplacementMap 滤镜了
feDisplacementMap 滤镜
feDisplacementMap 实际上是一个位置替换滤镜,就是改变元素和图形的像素位置的。
feDisplacementMap 滤镜在业界的主流应用是对图形进行形变,扭曲,液化。他比较重要的是 scale
参数,scale
属性定义了滤镜上的置换缩放因子。scale
越大,则偏移越大。
现在我们将 feTurbulence 湍流滤镜和我们的文本结合,就会产生如下效果。
<feDisplacementMap
scale="10"
in="OUTLINE_20"
in2="Texture_10"
result="OUTLINE_30"
/>
复制代码
feColorMatrix 滤镜
当谈到颜色的处理,feColorMatrix
是你最好的选择。feColorMatrix
是过滤中的一种类型,使用矩阵来影响颜色的每个通道(基于RGBA),你可以将其想象成 Photoshop 中通道编辑一样。
feColorMatrix 看起来像下方代码这样使用(原始图像RGBA的值默认为1)。矩阵计算 RGBA 每行的最终值,最后一个值是一个乘数。
<feColorMatrix
type="matrix"
values="R 0 0 0 0,
0 G 0 0 0,
0 0 B 0 0,
0 0 0 A 0 "
/>
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
复制代码
这里我们定义一个改变着色的滤镜,再用 feComposite 融合到文案中,我们用到了 arithmetic
组合,不用也可以,这里只是为了效果。
arithmetic
:该值表示属性中定义的源图形in
和属性中定义的目标图形in2
使用以下公式进行组合:result = k1 * in1 * in2 + k2 * in1 + k3 * in2 + k4
<feColorMatrix
type="matrix"
values="20 0 0 0 0,
0 20 0 0 0,
0 0 20 0 0,
0 0 0 1 0"
in="Texture_10"
result="Texture_20"
/>
<feComposite
operator="arithmetic"
k2="-1"
k3="1"
in="Texture_20"
in2="OUTLINE_30"
result="OUTLINE_40"
/>
复制代码
step2:绘制蓝色内容
feOffset 滤镜
feOffset 过滤器原语允许偏移输入图像。属性 dx 和属性 dy 的值指定了它的偏移量。
这里我们混入偏移量之后再给内容加上噪音。
<feOffset dx="-3" dy="4" in="SourceAlpha" result="FILL_10" />
<feDisplacementMap
scale="17"
in="FILL_10"
in2="Texture_10"
result="FILL_20"
/>
复制代码
feFlood 滤镜
该滤镜用 flood-color
元素定义颜色,用 flood-opacity
元素定义不透明度,通过这两个参数填充了滤镜子区域。
然后我们继续通过 feComposite 将准备好的颜色滤镜与文本合并,这里我们使用 in
参数
in
: 由in
属性定义的源图形部分与在in2
属性中定义的目标图形重叠,替换目标图形。
<feFlood
flood-color="#73DCFF"
flood-opacity="0.75"
result="COLOR-blu"
/>
<feComposite
operator="in"
in="COLOR-blu"
in2="FILL_20"
result="FILL_50"
/>
复制代码
最后我们想让 蓝色的内部文字与黑色的边框合并,这里我们就可以用到 feMerge 参数了
feMerge 滤镜
feMerge滤镜允许同时应用滤镜效果而不需要考虑顺序。
<feMerge result="merge2">
<feMergeNode in="FILL_50" />
<feMergeNode in="OUTLINE_40" />
</feMerge>
复制代码
step3: 添加上阴影
feConvolveMatrix 滤镜
这个过滤器原语是最强大和最难掌握的过滤器之一。它的主要目的是使您能够创建自己的过滤器。简而言之,您将定义一个像素栅格(内核矩阵),它会根据其相邻像素的值来改变像素。这样就可以创建自己的滤镜效果,例如模糊或锐化滤镜,或者创建挤压。
下面是 feConvolveMatrix 创建一个 45 度、8 像素深度的挤压。该 order
属性定义了宽度和高度,以便基元知道是应用 8×8 矩阵:
<feConvolveMatrix
order="8,8"
divisor="1"
kernelMatrix="
1 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 0
0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 1 "
in="SourceAlpha"
result="BEVEL_10"
/>
<feMorphology
operator="dilate"
radius="10"
in="BEVEL_10"
result="BEVEL_20"
/>
复制代码
这样挤压我们可能看不清效果,我们还需要通过 feComposite 的 out
属性来取出挤压值。
<feComposite
operator="out"
in="BEVEL_20"
in2="BEVEL_10"
result="BEVEL_30"
/>
<feDisplacementMap
scale="7"
in="BEVEL_30"
in2="Texture_10"
result="BEVEL_40"
/>
复制代码
因为我们希望它只拉伸到右侧和底部,所以我们必须偏移结果。
由于到了这里 IE 的兼容性有问题,所以为了保持跨浏览器的兼容性,我们将使用 feOffset 滤镜来处理偏移。
<feOffset dx="-7" dy="-7" in="BEVEL_40" result="BEVEL_60" />
<feComposite
operator="out"
in="BEVEL_60"
in2="OUTLINE_10"
result="BEVEL_70"
/>
复制代码
然后我们再通过 feMerge 将以上的滤镜做合并。
<feMerge result="merge2">
<feMergeNode in="BEVEL_70" />
<feMergeNode in="FILL_50" />
<feMergeNode in="OUTLINE_40" />
</feMerge>
复制代码
到此主要的滤镜就用完了,接下来我们只是为了打到效果重复使用滤镜即可。
step4:完成剩余配置
<feOffset dx="-9" dy="-9" in="BEVEL_10" result="BEVEL-FILL_10" />
<feComposite
operator="out"
in="BEVEL-FILL_10"
in2="OUTLINE_10"
result="BEVEL-FILL_20"
/>
<feDisplacementMap
scale="17"
in="BEVEL-FILL_20"
in2="Texture_10"
result="BEVEL-FILL_30"
/>
<feComposite
operator="in"
in="COLOR-red"
in2="BEVEL-FILL_30"
result="BEVEL-FILL_50"
/>
复制代码
最后
最后我们把所有的滤镜合并,再加上一个背景色,就可以实现上文说的效果,是不是很简单。
<feMerge result="merge2">
<feMergeNode in="BEVEL-FILL_50" />
<feMergeNode in="BEVEL_70" />
<feMergeNode in="FILL_50" />
<feMergeNode in="OUTLINE_40" />
</feMerge>
复制代码
如上就是所有步骤啦,全部代码都在这里,如果哪里看不懂可以再放 git 地址。
SVG 的过滤器其实十分强大,这里只是一个入门级教程,对此十分感兴趣可以查阅 MDN 教程。