跳到主要内容

滤镜合成层与重绘

filtermaskclipPath 以及组上的 opacity / mix-blend-mode,都会把子树先画到 离屏缓冲 再贴回。这是合成规则,不是「特效免费」。节点成本见 DOM 节点成本;组级 opacity 的颜色语义见 绘制顺序

1. 适用 / 不适用

适用: 少量装饰(一枚图标的软阴影、选中高光);导出前烘焙;静态图层上的一次性滤镜。

不适用: 每个数据点一份 feGaussianBlur;在会频繁改 viewBox 的相机层上挂全图滤镜;用 will-change: filter 常驻「优化」。

2. 什么会逼出离屏

触发发生什么
<g filter="url(#blur)">子树光栅进缓冲,按滤镜原语处理
mask / 非简单 clipPath先出 mask 缓冲再合成
opacity < 1先合成组,再整体淡出
mix-blend-mode隔离 + 与背景混合
isolation: isolate明确建组

叶子上的 opacity 通常比组上便宜,语义也不同。模糊半径按 用户空间 解释,再光栅;缓冲分辨率由引擎挑,不是 无限矢量。

export function attachBlur(
svg: SVGSVGElement,
target: SVGElement,
stdDeviation: number,
): string {
const doc = svg.ownerDocument;
const ns = "http://www.w3.org/2000/svg";
let defs = svg.querySelector("defs");
if (!defs) {
defs = doc.createElementNS(ns, "defs");
svg.prepend(defs);
}
const id = `blur-${crypto.randomUUID()}`;
const filter = doc.createElementNS(ns, "filter");
filter.id = id;
const blur = doc.createElementNS(ns, "feGaussianBlur");
blur.setAttribute("stdDeviation", String(stdDeviation));
filter.append(blur);
defs.append(filter);
target.setAttribute("filter", `url(#${id})`);
return id;
}

stdDeviation 稍大,离屏矩形就膨胀。动画中改偏差等于每帧重光栅。生产装饰能预渲染成 <image> 就不要实时滤镜。

3. 缩放后发糊

相机改 viewBox 或 CSS 盒子变大之后,旧滤镜缓冲被拉伸,发光边缘发糊、阴影成块。CSS transform: scale 整棵 SVG 更糟:命中、线宽、滤镜分辨率一起坏,见 viewport

对策:

  • 相机层不要挂全场景 filter
  • 必须滤镜时,缩放稳定后再上,或按缩放重建 filter 原语
  • will-change: transform 同样可能把 SVG 提升成位图层,动画结束后去掉,见 CSS 与 WAAPI
  • vector-effect: non-scaling-stroke 只管线宽,不管滤镜清晰度

4. 怎么测

不要猜。Chrome:

  1. Performance:勾选 Rendering,看 Paint 是否被 filter 拉长。
  2. Layers:彩色层边框;滤镜组常是独立层。层多、层大,滚动/缩放必卡。
  3. 对比:去掉 filter / 把组 opacity 改到叶子,同一相机手势跑一遍。
export function measureStylePaint(label: string, mutate: () => void): void {
performance.mark(`${label}-start`);
mutate();
performance.mark(`${label}-end`);
performance.measure(label, `${label}-start`, `${label}-end`);
}

这段只覆盖同步 JS。滤镜光栅看 Performance 里的 Paint / Update Layer Tree,不要用 measure 的毫秒当帧预算结论。

Safari 大 blur 更易卡;Android WebView 不要用桌面 Chrome 层数类推。

5. 失败形态

症状原因
平移还行,一加阴影掉帧每节点或整组实时 blur
放大后光晕成位图块离屏缓冲未按新分辨率重建
两个半透明圆重叠色不对opacity 写在 <g> 上先合成
Layers 里几十块全屏will-change / filter / 组 opacity 喷在容器上
导出 PDF 滤镜丢失或更糊打印链路不走浏览器那套滤镜光栅

权威资料

核对日期:2026-08-26