SVG滤镜不是简单的CSS属性映射,而是一套基于XML描述的像素级图像处理管线。当给一个SVG元素应用滤镜时,浏览器会先渲染该元素的原始图形和透明度通道,再按照filter元素内部定义的滤镜原语顺序依次处理,每个原语的输入都可以来自前一个原语的输出结果,最终把合成图像绘制到画布上。如果XML标签嵌套关系出错、坐标区域设置不合理或输入输出名称写错,滤镜效果就会出现异常,甚至导致整个元素不可见。

SVG滤镜的基本工作原理
在SVG 1.1规范中,滤镜的入口是<filter>元素,它本身不会直接产生视觉效果,而是充当一个容器,内部按顺序放置各种滤镜原语。原语是真正执行图像处理操作的单元,比如<feGaussianBlur>负责高斯模糊,<feOffset>负责像素位移。每个滤镜原语都有输入和输出两个基本概念:in属性指定输入源,result属性为当前结果命名,供后续原语引用。如果某个原语没有显式指定输入,SVG会使用链式规则自动将上一个原语的结果作为输入。
默认的输入源主要有两种。一种是SourceGraphic,表示元素未经滤镜处理的原始彩色图形,包括填充、描边和内部透明度;另一种是SourceAlpha,表示元素原始图形的透明度通道,以黑色不透明形式呈现。例如制作投影时,通常先对SourceAlpha做模糊,这样可以只处理轮廓而不受填充颜色干扰。理解这两个概念是组合滤镜链的基础。
以下是一个最基本的模糊滤镜写法,它只包含一个<feGaussianBlur>原语:
<filter id="blur"> <feGaussianBlur stdDeviation="3"/> </filter>
这段XML定义了一个名为blur的滤镜,对原始图形进行标准差为3的高斯模糊。使用时通过CSS的filter: url(#blur)或SVG属性filter="url(#blur)"引用。不过这个滤镜没有任何区域扩展,模糊后的边缘可能会被裁剪,后面会详细说明坐标问题。
filter元素与XML语法结构
<filter>元素可以接受多个属性,其中最核心的是坐标和单位相关属性。x、y、width、height定义了滤镜作用区域,默认值分别为-10%、-10%、120%、120%。也就是说,滤镜区域默认比元素包围盒向四周各扩展10%,这给阴影、发光等溢出效果留出了一定空间。如果模糊半径较大或位移距离超过10%,超出区域的部分会被裁剪掉,此时需要手动扩大区域。
另外两个属性控制坐标系类型。filterUnits决定x、y、width、height使用的坐标参考,默认值为objectBoundingBox,即元素包围盒百分比单位,0到1之间的数值表示相对比例。而primitiveUnits决定滤镜原语内部坐标和尺寸的参考系统,默认值为userSpaceOnUse,即使用当前用户坐标系中的绝对单位。例如stdDeviation="5"在默认primitiveUnits下表示5个用户单位,但如果把primitiveUnits设为objectBoundingBox,模糊半径就变成了包围盒边长的百分比,需要特别留意。
下面是一个完整的filter元素骨架示例,它定义了扩展区域和坐标单位:
<filter id="expandedBlur" x="-20%" y="-20%" width="140%" height="140%" filterUnits="objectBoundingBox" primitiveUnits="userSpaceOnUse"> <feGaussianBlur stdDeviation="6" result="blurResult"/> </filter>
这段代码将滤镜区域扩大为包围盒的140%,并明确指定了两种坐标单位。这样即使模糊半径达到6,也不会出现边缘截断。result="blurResult"为输出命名,后续原语可以通过in="blurResult"引用它。
常用滤镜原语的XML写法与参数
实际项目中很少只使用单一原语,投影、发光、色彩调整等效果都需要多个原语协作。以最常见的投影效果为例,它需要以下几个步骤:先提取透明度通道并模糊,然后将模糊结果偏移,再用纯色填充并通过复合运算限制在模糊区域内,最后与原图形合并。对应的XML代码如下:
<filter id="dropShadow">
<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/>
<feOffset dx="5" dy="5" result="offsetBlur"/>
<feFlood flood-color="#000000" flood-opacity="0.6"/>
<feComposite in2="offsetBlur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
这里<feGaussianBlur>使用SourceAlpha作为输入,对透明度通道进行半径为4的高斯模糊。<feOffset>将模糊结果沿x轴和y轴各移动5个单位,生成阴影偏移效果。<feFlood>生成一个纯色区域,并通过<feComposite>的operator="in"操作将这个纯色区域裁剪到偏移模糊结果的透明形状内,从而获得具有指定颜色和不透明度的阴影。最后<feMerge>把阴影和原始图形上下叠加,形成完整投影。
另一个常用的原语是<feColorMatrix>,它通过一个4行5列的矩阵对每个像素的RGBA通道进行线性变换,可以实现饱和度调整、灰度化、反相甚至自定义色彩映射。例如将图形转为灰度可以使用如下写法:
<filter id="grayscale">
<feColorMatrix type="matrix" values="0.33 0.33 0.33 0 0
0.33 0.33 0.33 0 0
0.33 0.33 0.33 0 0
0 0 0 1 0"/>
</filter>
type="saturate"配合values="0"也能实现灰度效果,但矩阵形式更灵活,可以调整不同通道的权重。需要注意的是矩阵数值之间的空格和换行在XML解析时会被视为空白分隔,只要保证有20个有效数字即可。
发光效果通常是模糊加合成的变体。可以先对原始图形进行模糊,然后将模糊结果与原图形合并,或者使用<feMerge>叠加多层不同半径的模糊来模拟柔和光晕。例如:
<filter id="glow">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" result="glow1"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="5" result="glow2"/>
<feMerge>
<feMergeNode in="glow2"/>
<feMergeNode in="glow1"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
这段代码生成了两个不同模糊半径的光晕层,先绘制较模糊的大光晕,再绘制较清晰的小光晕,最后叠加原始图形,形成从内向外逐渐消散的发光效果。通过调整stdDeviation和合并顺序,可以控制光晕的强度与范围。
滤镜链组合与性能优化
滤镜链的威力在于通过result和in灵活传递中间结果。当需要处理多个分支时,可以把某个原语的输出保存为具名结果,后续多个原语分别引用。例如既要模糊原始图形又要保留描边,可以先对图形做分支处理,再用<feBlend>或<feComposite>合成。不过过度嵌套会使XML结构难以维护,也增加浏览器的栅格化负担。
性能方面,SVG滤镜是在离屏缓冲区中进行像素级运算,滤镜区域越大、分辨率越高,计算量就越大。默认的滤镜区域是包围盒扩展10%,如果阴影偏移较大或者模糊半径很大,必须手动扩大区域,但同时也会带来更多像素处理。建议只扩展实际需要的范围,不要设置过大的width和height。此外,多个滤镜原语会创建多个中间图像,移动端设备上应尽量减少不必要的滤镜链,特别是避免在动画中频繁触发大范围模糊。
另一个常见的性能陷阱是使用filterUnits="objectBoundingBox"时忘记缩放原语参数。由于默认primitiveUnits是userSpaceOnUse,如果图形包围盒尺寸很小,像stdDeviation="10"这样的模糊会显得异常巨大。反之如果包围盒很大,模糊可能看起来不明显。在设计响应式图标或可变尺寸图形时,建议显式设置primitiveUnits,或者根据实际尺寸动态调整参数,确保滤镜效果在不同缩放级别下保持一致。
总之,掌握SVG滤镜的XML语法不仅要知道标签怎么嵌套,还要理解输入输出模型、坐标单位和区域设置。只有把这些细节吃透,才能写出稳定、高效、跨浏览器一致的SVG滤镜效果。
SVG滤镜效果SVG filterXML语法修改时间:2026-09-24 00:32:29