2026年5月27日 14:37

SVG 怎样实现渐变和滤镜效果?

为什么 SVG 需要渐变和滤镜

纯色填充和简单描边只能解决最基本的视觉需求。当设计要求柔和过渡的光影、逼真的投影、或是非写实的色彩处理时,SVG 的渐变和滤镜才是真正的答案——它们让矢量图形脱离"扁平图标"的刻板印象,具备接近位图编辑软件的表现力,同时保留分辨率无关的优势。

线性渐变 linearGradient

线性渐变沿一条直线方向过渡颜色。它定义在 <defs> 内部,通过 x1/y1/x2/y2 控制渐变线的起止坐标。

xml
<defs> <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%"> <stop offset="0%" stop-color="#ff6b6b" /> <stop offset="50%" stop-color="#feca57" /> <stop offset="100%" stop-color="#48dbfb" /> </linearGradient> </defs> <rect width="300" height="100" fill="url(#grad1)" />

这段代码实现了一个从左到右的红-黄-蓝水平渐变。x1="0%" x2="100%" 让渐变方向水平,把 y2 也设为 100% 就能得到对角线渐变。

径向渐变 radialGradient

径向渐变从一个中心点向外辐射,适合做球体高光、聚光灯等效果。

xml
<defs> <radialGradient id="grad2" cx="50%" cy="50%" r="50%" fx="40%" fy="40%"> <stop offset="0%" stop-color="#ffffff" /> <stop offset="100%" stop-color="#2d3436" /> </radialGradient> </defs> <circle cx="150" cy="100" r="80" fill="url(#grad2)" />

cx/cy/r 定义渐变圆的圆心和半径,fx/fy 是焦点位置——偏移焦点可以模拟定向光源照射球体的效果。当 fx/fycx/cy 不重合时,高光区会偏向焦点一侧。

渐变的关键控制属性

stop-color、stop-opacity 和 offset

每个 <stop> 节点通过 offset 指定在渐变线上的位置(0%–100%),stop-color 设定颜色,stop-opacity 控制该点的透明度。两个相邻 stop 之间的颜色会自动插值。

想让渐变在某段区间保持纯色,只需把两个 stop 设为相同的 stop-color 但不同的 offset

xml
<stop offset="30%" stop-color="#e74c3c" /> <stop offset="60%" stop-color="#e74c3c" />

这样从 30% 到 60% 都是纯红色,两侧才产生过渡。

gradientUnits

gradientUnits 决定坐标是相对于元素本身还是整个视口:

  • objectBoundingBox(默认):坐标 0–1 映射到元素的边界框
  • userSpaceOnUse:使用 SVG 画布的绝对坐标

当多个元素共享同一个渐变但尺寸不同时,userSpaceOnUse 能保证一致的渐变范围;objectBoundingBox 则自动适配每个元素。

gradientTransform 和 spreadMethod

gradientTransform 允许对渐变坐标施加矩阵变换(旋转、缩放等),等同于 CSS 的 transformspreadMethod 控制渐变范围外的填充方式:pad(默认,延伸最后一色)、repeat(重复)、reflect(镜像翻转重复)。

filter 滤镜的工作原理

SVG 滤镜基于"图元管道"(filter primitive pipeline)模型:每个滤镜原语接收输入图像,处理后输出结果,下一个原语再接续处理。整条管线定义在 <filter> 元素中,放在 <defs> 里。

xml
<defs> <filter id="myFilter" x="-20%" y="-20%" width="140%" height="140%"> <!-- 滤镜原语依次排列 --> </filter> </defs> <rect filter="url(#myFilter)" ... />

x/y/width/height 定义滤镜的计算区域,默认是 -10%/120%,如果模糊或偏移超出原元素边界,需要手动扩大这个区域。

两个内置输入输出标识符贯穿整个管道:

  • SourceGraphic:原始未过滤图形
  • SourceAlpha:原始图形的 Alpha 通道(无颜色)
  • result:当前原语的输出命名,供后续原语通过 in 引用

高斯模糊 feGaussianBlur

最常用的滤镜原语之一,stdDeviation 控制模糊半径,值越大越模糊:

xml
<filter id="blur1"> <feGaussianBlur in="SourceGraphic" stdDeviation="5" /> </filter>

可以分别指定水平和垂直方向的模糊:stdDeviation="8 2" 表示水平模糊 8px、垂直 2px。高斯模糊是构建投影、发光等效果的基础——先把图形模糊,再和原图叠加。

投影 feDropShadow

feDropShadow 是一个复合原语,内部等价于 feOffset + feGaussianBlur + feFlood + feComposite 的组合:

xml
<filter id="shadow1"> <feDropShadow dx="4" dy="4" stdDeviation="3" flood-color="#000000" flood-opacity="0.4" /> </filter>

dx/dy 控制偏移,flood-color/flood-opacity 控制阴影颜色和透明度。需要内阴影或更复杂的投影时,就得手动拆分上述原语组合,灵活控制每一步。

颜色矩阵 feColorMatrix

feColorMatrix 是 SVG 滤镜中最强大的色彩处理工具,支持四种模式:

matrix 模式

用 5×4 矩阵对每个像素的 RGBA 做线性变换:

shell
| R' | | r1 r2 r3 r4 r5 | | R | | G' | = | g1 g2 g3 g4 g5 | × | G | | B' | | b1 b2 b3 b4 b5 | | B | | A' | | a1 a2 a3 a4 a5 | | A | | 1 |

单位矩阵(无效果):values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"

灰度转换只需让 R/G/B 三个通道取加权平均:

xml
<feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0" />

saturate 模式

type="saturate" values="0" 完全去色,values="2" 饱和度翻倍。值域 0–1 降低饱和度,>1 增强饱和度。

hueRotate 模式

type="hueRotate" values="90" 将色相旋转 90 度,值域 0–360。

luminanceToAlpha 模式

将亮度映射为透明度,常用于生成基于亮度的蒙版。

注意:feColorMatrix 默认在 linearRGB 色彩空间计算,做反色等操作时结果可能偏离预期,在 <filter> 上设置 color-interpolation-filters="sRGB" 可切换。

混合 feBlend

feBlend 将两个输入按指定模式混合,支持 normal/multiply/screen/darken/lighten/overlay/color-dodge/color-burn/hard-light/soft-light/difference/exclusion/hue/saturation/color/luminosity 共 16 种模式。

xml
<filter id="blend1"> <feGaussianBlur in="SourceAlpha" stdDeviation="6" result="blur" /> <feOffset in="blur" dx="3" dy="3" result="offsetBlur" /> <feFlood flood-color="#e74c3c" flood-opacity="0.6" result="color" /> <feComposite in="color" in2="offsetBlur" operator="in" result="shadow" /> <feBlend in="SourceGraphic" in2="shadow" mode="normal" /> </filter>

这个例子先模糊 Alpha 通道、偏移、着色,最后用 feBlend 把彩色投影和原图合成。in2 指定第二个输入——每一步都可以精确控制数据来源。

滤镜组合实战

把多个原语串起来才能实现复杂效果。一个完整的发光+投影滤镜长这样:

xml
<filter id="glowShadow" x="-30%" y="-30%" width="160%" height="160%"> <!-- 投影:偏移+模糊 --> <feOffset in="SourceAlpha" dx="4" dy="6" result="offset" /> <feGaussianBlur in="offset" stdDeviation="5" result="shadowBlur" /> <feFlood flood-color="#000000" flood-opacity="0.35" result="shadowColor" /> <feComposite in="shadowColor" in2="shadowBlur" operator="in" result="shadow" /> <!-- 发光:模糊+着色 --> <feGaussianBlur in="SourceAlpha" stdDeviation="8" result="glowBlur" /> <feFlood flood-color="#6c5ce7" flood-opacity="0.5" result="glowColor" /> <feComposite in="glowColor" in2="glowBlur" operator="in" result="glow" /> <!-- 分层合成:投影 → 发光 → 原图 --> <feMerge> <feMergeNode in="shadow" /> <feMergeNode in="glow" /> <feMergeNode in="SourceGraphic" /> </feMerge> </filter>

feMerge 是另一种合成方式,按顺序将多个输入叠加到同一画布上,先写的在底层。这里投影在最下,发光居中,原图最上。

CSS filter 与 SVG filter 怎么选

CSS filter 属性提供了 blur/brightness/contrast/drop-shadow/grayscale/hue-rotate/invert/opacity/saturate/sepia 等快捷函数,本质上就是 SVG 滤镜的常用子集,浏览器做了硬件加速优化。

选择 CSS filter 的场景:

  • 只需要单种简单效果(如 filter: blur(4px)
  • 追求渲染性能,CSS filter 解析和执行更快
  • 不需要跨元素复用同一滤镜定义

选择 SVG filter 的场景:

  • 需要多步管道组合(模糊+偏移+着色+混合)
  • 需要颜色矩阵等 CSS filter 无法表达的效果
  • 多个元素复用同一定义
  • 需要兼容旧版浏览器(SVG filter 起步更早)

简单效果用 CSS,复杂效果用 SVG——这是最务实的分工。

性能注意事项

  1. 模糊是性能杀手stdDeviation 超过 20 的模糊在移动端会显著卡顿,能用小值就别用大值
  2. 缩小滤镜区域:精确设置 <filter>x/y/width/height,避免对不可见区域做无用计算
  3. 减少滤镜层级:每多一个原语就多一轮像素处理,能用 3 步完成的效果别拆成 7 步
  4. 避免在动画中使用复杂滤镜:每帧都要重新计算像素,优先用 CSS transform/opacity 做动画
  5. 硬件加速差异:CSS filter 在主流浏览器中走 GPU 加速路径,SVG filter 的加速程度取决于浏览器实现,Chrome 和 Firefox 的表现好于 Safari
  6. 测试移动端:SVG filter 在低端移动设备上的性能差距会被放大,务必真机验证

渐变让 SVG 拥有色彩过渡的能力,滤镜让 SVG 拥有像素级处理的能力。两者组合起来,矢量图形不再只是线条和填色——模糊、投影、色彩变换、多步合成,这些曾经需要位图编辑器才能完成的效果,现在直接写在 SVG 标记里就能实现。掌握 stop 节点控制渐变节奏,理解 filter primitive pipeline 的输入输出串联,比记住任何单个属性都重要。

标签:SVG