滤镜进阶-位移滤镜
通过之前几节课的学习,我相信你和我有着同样的感受。SVG 滤镜效果既神奇又奇特,还有许多东西需要我们继续探索。学习和使用 SVG 滤镜需要勇气,敢于进入这个充满未知和不确定性的世界。你的探索精神将不断受到意想不到效果的考验,必须学习复杂的知识和技巧。然而,一旦掌握了它,你将获得前所未有的力量——只需几行代码就能改变 Web UI 和整个应用的视觉效果。
位移滤镜简介
在开始介绍位移滤镜(<feDisplacementMap>)之前,我们先来看两个 <feDisplacementMap> 滤镜的效果:
按钮和用户头像都是 Web 中的常见 UI,应用了位移滤镜之后的,UI 效果是不是立马显得高大上了。位移滤镜效果除了可以应用于常见的 UI 元素上之外,还可以制作一些酷炫的动画效果。例如下面这个效果,由 @yoksel 在 Codepen 上提供:
Demo 地址:https://codepen.io/yoksel/full/MLVjoB (来源于 @yoksel )
是不是很酷炫!这两个案例的效果都离不开位移滤镜 <feDisplacementMap> 的功劳。那么,什么是位移滤镜呢?
简单地说,位移滤镜实际上是一个置换滤镜,通过改变图像的像素位置来实现诸如 Photoshop 扭曲滤镜那样的变形、旋转或波纹等各种有趣的视觉效果。
以 <feDisplacementMap> 滤镜实现扭曲效果为例,它需要两个图像作为输入:
- 实际需要被扭曲的源图像,由
<feDisplacementMap>滤镜基元的in属性指定 - “位移图像”,由
<feDisplacementMap>滤镜基元的in2属性指定,它可以是噪声图像或任何其他图像。该图像的像素值决定了源图像像素的偏移量
例如:
<!-- 源图像,需要被扭曲的图像 -->
<img src="https://picsum.photos/id/823/600/600" alt="" class="filtered" />
<svg class="sr-only">
<defs>
<filter id="filter" x="-20%" y="-20%" width="140%" height="140%" color-interpolation-filters="sRGB">
<!-- 位移图像 -->
<feImage href="https://picsum.photos/id/41/600/600" result="IMAGE" />
<!-- 位移滤镜 -->
<feDisplacementMap id="fedisplacementmap" in="SourceGraphic" in2="IMAGE" xChannelSelector="R" yChannelSelector="B" scale="50" />
</filter>
</defs>
</svg>使用 CSS 的 filter 属性,将 SVG 的 <filter> 定义的滤镜(#filter)应用到源图像 .filtered 上:
.filtered {
filter: url("#filter");
}你将在浏览器中看到,源图像被扭曲了,看上去就像一个水波形状:
示例中的滤镜构建如下图所示:
首先,使用了 <feImage> 滤镜基元引入一张图像,这个滤镜的结果被命名为 IMAGE ,它被 <feDisplacementMap> 滤镜基元的 in2 属性引用,用作位移图。而实际扭曲是在这里发生:
- 正负的
scale属性定义了扭曲的强度。在这个示例中,设置了scale="50" xChannelSelector和yChannelSelector属性用于确定输入图像的颜色通道(R、G、B 和 A)中哪一个应用该用于哪个轴进行扭曲。两个属性默认使用位移图的透明通道(A),这意味着,如果你使用了没有透明通道的位移图,并且省略了这些属性(未显式设置),那么你只会看到源图像的对象线偏移。在这个示例中,设置 了xChannelSelector="R"和yChannelSelector="B",它们会告诉浏览器,用位移图的红色通道控制水平方向位移,用蓝色通道控制垂直方向位移
以这种方式扭曲图像可能会很有趣,但结果是不可预测的,而且大多数情况下,视觉效果并不美观。那么,有没有办法对输出进行像素级的精确控制呢?
SVG 规范是这么说的:
SVG 规范:https://drafts.fxtf.org/filter-effects/#elementdef-fedisplacementmap
简单地说,<feDisplacementMap> 滤镜基元使用来自 in2 图像(位移图)的像素值来空间上位移 in 图像(源图像)。在这个位移过程中,会通过下面这个公式来执行变换:
也就是说,<feDisplacementMap> 滤镜基元对图像进行位置映射时,会通过上面这个公式来来进行映射。这意味着,只要我们理解了这个映射公式,就能对输出进行像素级的精确控制。
简单解释一下:
P'(x,y)表示结果图像中一个像素的x,y坐标P(x,y)表示输入图像in(源图像)中该像素的x,y坐标XC和YC是位移图中给定像素的归一化(1 ÷ 255)RGB 颜色值,即由xChannelSelector和yChannelSelector指定的通道的分量值。XC(x,y)表示当前x,y坐标像素点其X轴方向上设置的对应通道的计算值,范围是0~1YC(x,y)表示当前x,y坐标像素点其Y轴方向上设置的对应通道的计算值,范围是0~1x + scale × (XC(x,y) - 0.5), y + scale × (YC(x,y) - 0.5)指的是具体的转换规则-0.5是偏移值,因此XC(x,y) - 0.5范围是-0.5~0.5,YC(x,y) - 0.5范围也是-0.5~0.5scale表示计算后的偏移值相乘的比例,scale越大,则偏移越大
最后,操作结果必须取反(基本上就是公式中的每个 + 号都需要替换成 - 号),即每个加法都变成减法,那么公式将变成:
这样做的目的是为了反向应用位移效果,使得每个像素的位置变成其原始位置的相反方向上的相应位置。这通常是在处理特定图像效果时所需要的。
接下来,将通过一些简单的小实验,通过将原始位移图输入到滤镜中来验证上面这个公式。注意,这些原始位移图只包含一种颜色。例如:
<svg class="sr-only">
<defs>
<filter id="filter" x="-20%" y="-20%" width="140%" height="140%" color-interpolation-filters="sRGB">
<feImage href="https://assets.codepen.io/100347/fedm_rgb515151.png" result="IMAGE" width="600" height="600" />
<feDisplacementMap id="fedisplacementmap" in="SourceGraphic" in2="IMAGE" xChannelSelector="R" yChannelSelector="B" scale="100" />
</filter>
</defs>
</svg>上位代码中 <feImage> 引入的位移图是具有 rgb(51 51 51) 的纯色图像,当 <feDisplacementMap> 滤镜的 scale 值为 100 时,如何预期源图像在 x = 100 和 y=100 处的像素坐标会被转换?
我们把 <feDisplacementMap> 滤镜基元中的相关参数值套入上面公式进行计算:
scale = 100
x = 100
y = 100
XC = 51 ÷ 255
YC = 51 ÷ 255
X = x - scale × (XC - 0.5) = 100 - 100 × (51 ÷ 255 - 0.5) = 130
Y = y - scale × (YC - 0.5) = 100 - 100 × (51 ÷ 255 - 0.5) = 130结果就是 P'(x,y) = P'(130,130) ,即结果像素被移动到 (130,130) 坐标点。这意味着它将向右移动 30px ,并向下移动 30px :
现在,将 <feImage> 引入的位移图调整 rgb(127 127 127) 纯色图像(大概是 50% 的灰色)。
<svg class="sr-only">
<defs>
<filter id="filter" x="-20%" y="-20%" width="140%" height="140%" color-interpolation-filters="sRGB">
<feImage href="https://assets.codepen.io/100347/fedm_grey.png" result="IMAGE" width="600" height="600" />
<feDisplacementMap id="fedisplacementmap" in="SourceGraphic" in2="IMAGE" xChannelSelector="R" yChannelSelector="B" scale="100" />
</filter>
</defs>
</svg>和之前一样,将相关参数套入公式进行计算:
scale = 100
x = 100
y = 100
XC = 127 ÷ 255
YC = 127 ÷ 255
X = x - scale × (XC - 0.5) = 100 - 100 × (127 ÷ 255 - 0.5) = 100.196 ≈ 100
Y = y - scale × (YC - 0.5) = 100 - 100 × (127 ÷ 255 - 0.5) = 100.196 ≈ 100很显然,中性色(rgb(127 127 127))对图像不会产生明显的变化。肉眼之下,目标结果像素将保持不变:
请继续,这次我们把 <feImage> 引入的图像调得更亮一些,例如 rgb(204 204 204) ,颜色通道的值大于 127 :
<svg class="sr-only">
<defs>
<filter id="filter" x="-20%" y="-20%" width="140%" height="140%" color-interpolation-filters="sRGB">
<feImage href="https://assets.codepen.io/100347/fedm_rgb204204204.png" result="IMAGE" width="600" height="600" />
<feDisplacementMap id="fedisplacementmap" in="SourceGraphic" in2="IMAGE" xChannelSelector="R" yChannelSelector="B" scale="100" />
</filter>
</defs>
</svg>同样根据公式计算出最终值:
scale = 100
x = 100
y = 100
XC = 204 ÷ 255
YC = 204 ÷ 255
X = x - scale × (XC - 0.5) = 100 - 100 × (204 ÷ 255 - 0.5) = 70
Y = y - scale × (YC - 0.5) = 100 - 100 × (204 ÷ 255 - 0.5) = 70最终坐标 P'(70,70) 与初始坐标 P(100,100) 相比,很明显它会向左移动 30px ,并向上移动 30px :
通过这三个简单的小实验(示例),可以得到一个初浅的结论:
- 任何大于
127的颜色值都会将相应像素向缩放值(scale)的方向移动 - 任何小于
127的颜色值都会将相应像素向缩放值(scale)相反的方向移动 - 颜色值为
127图像像素几乎不会有任何移动
凭直觉,大家可能会认为黑色不会有任何效果,但现在应该很清楚,事实并非如此。实际上,黑色和白色将导致最大可能的向缩放值(scale)方向或相反方向的移动。
正如你所看到,纯黑色位移图使图像向右并向下移动移动 50px ,而白色位移图使图像向左并向上移动 50px 。
上面我们展示的都是纯色的位移图对源图像颜色像素位置的影响。接下来,我们再来看一个示例,进一步加深大家对 <feDisplacementMap> 滤镜的 xChannelSelector,yChannelSelector 和 scale 属性的理解。
我们把前面的纯色位移图换成像下面这样的一张图:
左半边是 rgb(255 127 127) ,该颜色的 R 通道的计算值是 1 ,G 和 B 通道的计算值是 0.5 ;右半边则是完全透明 rgb(255 255 255 ``/ 0``) 。
<svg class="sr-only">
<defs>
<filter id="filter" x="-20%" y="-20%" width="140%" height="140%" color-interpolation-filters="sRGB">
<feImage href="data:image/png;base64,..." result="IMAGE" width="600" height="600" />
<feDisplacementMap id="fedisplacementmap" in="SourceGraphic" in2="IMAGE" xChannelSelector="R" yChannelSelector="G" scale="100" />
<feComposite in="SourceGraphic" operator="in" in2="IMAGE" />
</filter>
</defs>
</svg>尝试着调整 scale 的值,你看到的效果如下:
并不难发现,应用滤镜的图片只是单纯的沿着水平方向(x 轴)移动。为什么呢?
这是因为xChannelSelector="R",yChannelSelector="G",水平方向基于图像颜色的 R 通道进行位移,垂直方向基于图像颜色的 G 通道进行位移。我们应用的位移图颜色(rgb(255 127 127)),其 R 通道的计算值是 1 ,G 通道的计算值是 0.5 。套用公式:
XC(x,y) = 1
YC(x,y) = 0.5
P′(x,y) ← P(x + scale × (XC(x,y) − 0.5),y + scale × (YC(x,y) − 0.5))
P′(x,y) ← P(x + scale × (1 − 0.5),y + scale × (0.5 − 0.5))
P′(x,y) ← P(x + scale × 0.5,y + 0)
P′(x,y) ← P(x + scale × 0.5,y)最终结果的坐标是 P(x + scale × 0.5,y) 。因此,调整 scale 的值,最终坐标只是 x 轴的值在变化。所以,你看到的图片只在水平方向发生了移动。
我想,通过这些简单的小实验,你对位移滤镜有所了解了。但问题是,到目前为止,还是不知道如何使用位移滤镜实现扭曲和水波纹等有趣的视觉效果。
在具体讲解如何制作水波纹 UI 效果之前,有一个概念需要与大家一起探讨——绝对图像映射!这对于位移滤镜而言,是一个非常重要的东西,因为它是所有后续效果的基础。这意味着,只有了解和掌握了这个概念,才能使用位移滤镜制作引人注目的 UI 效果。
绝对图像映射
绝对图像映射(Absolute Map)又称为恒等图像映射(Identity Map)。它既是一张特殊图像,将成为我们接下来所见所有效果的基础。这种映射执行一个非常简单的扭曲操作:按比例缩放图像。通过这种映射,可以实现多种视觉效果,并且它非常容易理解和应用。
为了在所有方向上等比缩放图像,颜色值必须从一边的最大值逐渐减少到对边的最小值。例如:
x轴使用红色,那么它的颜色值应该从最大值rgb(255 0 0)(左侧)逐渐减少到最小值rgb(255 0 0 ``/ 0``)(右侧)y轴使用蓝色,那么它的颜色值也应该从最大值rgb(0 0 255)(顶部)逐渐减少到最小值rgb(0 0 255 ``/ 0``)(底部)
这张利用红色和蓝色通道制作的绝对映射图,看起来大致像下图这样:
正如你所看到的,上图将使用红色表示 x 轴(水平方向),蓝色表示 y 轴(垂直方向)。但最终 xChannelSelector 和 yChannelSelector 选择哪种颜色都无关紧要。
通常这个绝对映射图可以按照下面几个步骤来制作:
注意,上图中第一矩形填充了一个从左(rgb(255 0 0))到右( rgb(255 0 0 ``/ 0``))的线性渐变;第二个矩形填充了一个从上(rgb(0 0 255))到下( rgb(0 0 255 ``/ 0``))的线性渐变,并将该矩形(它的图层)的混合模式设置为 screen 。请记住这个制作过程,稍后在 SVG 中制作绝对映射图像步骤与在 Figma 中制作步骤相同。
从 Figma 导出这个图形,这样你就创建了一个绝对映射图像!这个图像将作为各种图像扭曲的基础。换句话说,你可以在这个图像上制作图其他的位移图像:
看到上图,你是否产生了新的疑惑,“绝对”图像映射难道不是唯一的?不是的,这里所说的绝对图像映射并不指唯一性,而是指一种特定的图像变换技术,用于对图像进行比例缩放。这个技术确保每个像素点都按比例进行移动,但图像的整体形状和相对位置保持不变。你可以将其理解为一种怛等变换,即不引入复杂的扭曲或变形。
为了更好地理解这个过程,我写了一个小示例,给 <feImage> 滤镜基元提供了多种不同的位移图像。而且这些位移图像并不都是红蓝渐变或扭曲的,还提供了黑白渐变以及其他形状。请尝试选择下面 Demo 中的位移图像,查看 <feDisplacementMap> 滤镜对源图像产生的效果:
SVG 位移图映射
上面案例所展示的位移图(<feImage> 引用的图片)基本上都是传统的位图格式,例如 PNG 和 JPG 等。其实,我们还可以直接使用 SVG 创建位移图。
使用 SVG 创建位移图与之前在 Figma 中创建位移图步骤是相似的:
- 1️⃣:使用
<rect>创建两个矩形 - 2️⃣:使用
<linearGradient>创建两个线性渐变,其中一个水平渐变,从rgb(255 0 0 )到rgb(255 0 0 ``/ 0``),另一个中垂直渐变,从rgb(0 0 255)到rgb(0 0 255 ``/ 0``)。将它们应用到<rect>创建的矩形元素上 - 3️⃣:在第二个矩形上应用 CSS 的
mix-blend-mode: screen,将它们合并
上面步骤对应的 SVG 代码如下:
<svg viewBox="0 0 512 512" width="512" height="512">
<defs>
<!-- 创建水平渐变:rgb(255 0 0) 👉 rgb(255 0 0 / 0) -->
<linearGradient id="red-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="rgb(255 0 0)" />
<stop offset="100%" stop-color="rgb(255 0 0 / 0)" />
</linearGradient>
<!-- 创建垂直渐变: rgb(0 0 255) 👉 rgb(0 0 255 / 0) -->
<linearGradient id="blue-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="rgb(0 0 255)" />
<stop offset="100%" stop-color="rgb(0 0 255 / 0)" />
</linearGradient>
<symbol id="image" viewBox="0 0 512 512">
<g>
<rect id="black-rect" x="0" y="0" width="100%" height="100%" fill="#000" class="black" />
<rect id="red-rect" x="0" y="0" width="100%" height="100%" fill="url(#red-gradient)" class="rect--red" />
<rect id="blue-rect" x="0" y="0" width="100%" height="100%" fill="url(#blue-gradient)" class="rect--blue" style="mix-blend-mode: screen;"/>
</g>
</symbol>
</defs>
</svg>SVG 位移图准备好了,但是将其应用到滤镜中效果,并不如你所期望的那样:
<svg viewBox="0 0 512 512" width="512" height="512">
<defs>
<!-- 创建水平渐变:rgb(255 0 0) 👉 rgb(255 0 0 / 0) -->
<linearGradient id="red-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="rgb(255 0 0)" />
<stop offset="100%" stop-color="rgb(255 0 0 / 0)" />
</linearGradient>
<!-- 创建垂直渐变: rgb(0 0 255) 👉 rgb(0 0 255 / 0) -->
<linearGradient id="blue-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="rgb(0 0 255)" />
<stop offset="100%" stop-color="rgb(0 0 255 / 0)" />
</linearGradient>
<symbol id="image" viewBox="0 0 512 512">
<g>
<rect id="black-rect" x="0" y="0" width="512" height="512" fill="#000" class="black" />
<rect id="red-rect" x="0" y="0" width="512" height="512" fill="url(#red-gradient)" class="rect--red" />
<rect id="blue-rect" x="0" y="0" width="512" height="512" fill="url(#blue-gradient)" class="rect--blue" style="mix-blend-mode: screen;" />
</g>
</symbol>
<filter id="filter" color-interpolation-filters="sRGB">
<feImage href="#image" result="IMAGE__10" />
<feDisplacementMap in="SourceGraphic" in2="IMAGE__10" xChannelSelector="R" yChannelSelector="B" scale="100" result="DISPLACEMENT__MAP__10" />
</filter>
</defs>
</svg>const feImage = document.querySelector("feImage");
const url = feImage.getAttribute("href");
fetch(url)
.then((response) => {
return response.text();
})
.then((svgText) => {
const uri = encodeURIComponent(svgText);
feImage.setAttribute("href", `data:image/svg+xml;charset=utf-8,${uri}`);
})
.catch((error) => {
feImage.setAttribute("href", someFallbackURI);
});非常遗憾的是,到写这节课内容的时候,也仅能在 Safari 浏览器中看到效果:
因此,针对 SVG 位移图映射,我们就不展开阐述了!
其实,除了使用位图和 SVG 图形制作位移图之外,还可以使用 SVG 的滤镜来制作,例如 <feTurbulence> 滤镜。就我个人而言,我更倾向于使用其他滤镜来制作的位移图。接下来的示例,主要会围绕着其他滤镜制作位移图展开。
案例一:故障效果
通过上一节课的学习,我们知道如何使用 <feComponentTransfer> 和 <feColorMatrix> 等滤镜制作故障图效果。在这里,我们来看看,位移滤镜 <feDisplacementMap> 是如何基于其他 SVG 滤镜来制作一个像下面这样的文本故障效果:
这是一个简单的文本故障效果,直接上代码:
<svg class="sr-only">
<defs>
<filter id="filter" color-interpolation-filters="sRGB" >
<feFlood flood-color="rgb(128 0 128)" result="BASE-COLOR" />
<feFlood flood-color="rgb(255 0 128)" result="X-TRANSFORM">
<animate attributeName="y" values="0; 150; 40; 110; 0" dur="2.5s" repeatCount="indefinite" begin="0" />
<animate attributeName="height" values="10; 30; 15; 40; 10;" dur="4s" repeatCount="indefinite" begin="0" />
</feFlood>
<feMerge result="MERGE">
<feMergeNode in="BASE-COLOR" />
<feMergeNode in="X-TRANSFORM" />
</feMerge>
<feDisplacementMap in="SourceGraphic" in2="MERGE" scale="8" xChannelSelector="R" yChannelSelector="B">
<animate attributeName="scale" values="-8; 5.5; 0; 8; 4.5; 7; -4; -9; 5; -5.5; 19; -8" dur="6s" repeatCount="indefinite" begin="0" />
</feDisplacementMap>
</filter>
</defs>
</svg>简单解释一下上面的代码。
首先使用了 <feFlood> 滤镜基元,甚至源图像(SourceGraphic)创建了两个填充效果,其中第一个填充的颜色是 rgb(128 0 128) ,第二个填充的颜色是 rgb(255 0 128) 。同时使用 <animate> 元素动态化改变第二个 <feFlood> 填充物的 y 和 height 值。
接着使用 <feMerge> 将前面定义的两个填充物合并在一起,并且命名为 MERGE 。这相当于使用 <feFlood> 和 <feMerge> 创建了一个动画化的位移图像。它看起来像下面这样:
最后,将 MERGE 作为位移图像,被 <feDisplacementMap> 滤镜引用,从而产生扭曲效果。其中 scale 属性指定了位移的程度,xChannelSelector 和 yChannelSelector 属性分别指定了 X 和 Y 通道的颜色值的选择方式。同时,使用 <animate> 元素为位移映射效果添加了动画效果,改变了 scale 属性的值。最终使实现了一个文本障碍的效果。
案例二:给按钮添加云雾效果
在课程开始的时候,向大家展示了一个按钮和用户头像悬浮的动画效果。
这是一个 CSS 和 SVG 滤镜相互结合产生的一个交互动画效果。我们以上图中的按钮效果为例:
<button class="button">Hover Me</button>按钮基本样式就不在这里展示了,但按钮悬浮状态的云雾效果相关的 CSS 代码还是有必要贴一下:
.button {
--height: 100px;
--width: 200px;
width: calc(0.8 * var(--width));
height: calc(0.7 * var(--height));
transition: all 0.3s cubic-bezier(0, 0.22, 0.3, 1);
transform: translateZ(0);
overflow: hidden;
&::after {
content: "";
position: absolute;
width: 0;
height: 0;
filter: url(#filter);
border-radius: 50%;
z-index: -1;
transition: all 1.5s cubic-bezier(0.1, 0.22, 0.3, 1);
}
&:hover::after {
width: calc(2 * var(--width));
height: calc(2 * var(--height));
}
&:nth-child(1):hover::after {
left: -50%;
top: -50%;
background: #27ae60;
}
&:nth-child(2):hover::after {
right: -50%;
top: -50%;
background: #c0392b;
}
&:nth-child(3):hover::after {
left: -50%;
bottom: -50%;
background: #34495e;
}
&:nth-child(4):hover::after {
right: -50%;
bottom: -50%;
background: #2980b9;
}
}注意,我们使用 CSS 伪元素 ::after 添加了一个层,这个层应用了 #filter 滤镜,这个滤镜的主要效果是由 <feTurbulence> 和 <feDisplacementMap> 滤镜制作, 根据 <feTurbulence> 生成的噪声图像对源图形进行位移,产生扭曲效果。使 ::after 在视觉上具有一种类似波纹、水波或云雾等效果。具体代码如下所示:
<svg class="sr-only">
<defs>
<filter id="filter" color-interpolation-filters="sRGB">
<feTurbulence type="fractalNoise" baseFrequency=".01" numOctaves="6" in="SourceGraphic" result="TURBULENCE"/>
<feDisplacementMap in="SourceGraphic" in2="TURBULENCE" result="DISPLACEMENT_MAP" scale="100" />
</filter>
</defs>
</svg>其中 <feTurbulence> 生成一个具彩色效果的纹理图,它还没有应用到 <feDisplacementMap> 滤镜上时,按钮悬浮状态的效果如下:
像不像七彩云朵的效果。
注意,在我们这个示例中,<feTurbulence> 生成的彩色纹理图只是作为位移图像,<feDisplacementMap> 根据位移图像对伪元素 ::after 自身(源图形)进行位移,产生扭曲效果。再结合 CSS 位置的变化,从而实现我们所需要的按钮效果:
在这个基础上,可以演变出其他扭曲效果。例如,下面这个示例,我们分别给 <feTurbulence> 滤镜的 baseFrequency 和 numOctaves 以及 <feDisplacementMap> 滤镜的 xChannelSelector 、 yChannelSelector 和 scale 等属性的值提供调节器,你可以通过调整相关的参数查看最终的效果:
小结
有关于位移滤镜我们就介绍到这里了。简单回顾一下这节课的内容。
我们主要探讨了 SVG 滤镜中 <feDisplacementMap> 滤镜基元。首先,介绍了位移滤镜的基本原理,即通过位移图像的像素值移动源图像的像素,产生扭曲效果。然后,详细讲解了如何定义和应用滤镜,包括使用 <feTurbulence> 生成噪声图像,并将其与 <feDisplacementMap> 结合来实现位移效果。
后续章节的课程中还会继续应用到位移滤镜 <feDisplacementMap> 相关特性。你将在后续的课程中进一步的了解该滤镜,并通过与其他滤镜相结合,创作出更多有趣的 UI 视觉效果!