H5中SVG滤镜实现火焰或水波效果的核心是用<feTurbulence>生成动态噪声纹理,再通过<feDisplacementMap>进行像素级位移扰动;水波用turbulence类型+微调baseFrequency模拟起伏,火焰用fractalNoise+多层叠加+色彩矩阵模拟色温过渡。

在 H5 中用 SVG 滤镜实现火焰燃烧或水波扭曲,核心不是“贴图动画”,而是利用 <feTurbulence> 生成动态噪声纹理,再通过 <feDisplacementMap> 将其作用于目标图形,产生像素级位移——这种基于物理建模的扰动方式,比 CSS 动画或 GIF 更轻量、更可控,也更易与交互绑定。
水波扭曲:基础结构 + 动态 baseFrequency
水波效果依赖低频、缓慢变化的噪声。关键在于让 baseFrequency 随时间轻微浮动,模拟水面自然起伏:
- 在隐藏
<svg>中定义滤镜,<feTurbulence>的type="turbulence"(非 fractalNoise)更适合水波的连贯感 -
baseFrequency="0.015 0.02"是起始值,x/y 分量可略有差异,避免规律性条纹 - 用 JavaScript 每帧微调这两个值:例如 x 方向加
0.0008 * cos(t),y 方向加0.0012 * sin(t),t 为递增帧数 -
<feDisplacementMap>的scale控制扭曲强度,建议设为 12–25;xChannelSelector="G"、yChannelSelector="B"可让不同通道驱动不同方向,增强流动错觉
火焰燃烧:高频噪声 + 多层叠加 + 色彩干预
火焰需要更躁动、分形感更强的扰动,并配合颜色渐变和透明度变化:
- 改用
type="fractalNoise",numOctaves="3"增加细节层次,baseFrequency="0.04 0.06"提升活跃度 - 叠加两套
<feTurbulence>:一套慢速大波(主形态),一套快速小噪点(边缘闪烁),用<feMerge>合并后作为 displacement 输入 - 在 displacement 前插入
<feColorMatrix type="matrix">,将噪声图的绿色通道提亮、蓝色通道压暗,模拟火焰上部偏黄、底部偏橙红的色温过渡 - 最终用
<feComposite operator="in">把扭曲后的图形限制在原始火焰形状内,避免溢出失真
性能与兼容性要点
这类动态滤镜在移动端易卡顿,必须主动优化:
- 禁用
feDisplacementMap的动画在滚动容器或列表项中;只对静止区域(如 banner、图标、卡片封面)启用 - 给
<filter>显式设置x="-30%" y="-30%" width="160%" height="160%",防止模糊/位移被默认裁剪区域截断 - Chrome/Firefox/Safari 支持完整,但 Android WebView(尤其旧版)可能不支持
feDisplacementMap动画;可用if (SVGElement.prototype.hasAttribute)简单检测后降级为 CSSfilter: blur(2px) - 避免每帧都调用
setAttributeNS修改多个属性;把baseFrequency拼成单字符串再写入,减少 DOM 操作开销
最小可运行示例(水波)
复制以下代码到 HTML 文件即可看到实时水波倒影:
<div class="target"><img src="your-image.jpg"></div><svg width="0" height="0" style="position:absolute">
<defs>
<filter id="water" x="-30%" y="-30%" width="160%" height="160%">
<feTurbulence type="turbulence" baseFrequency="0.015 0.02" numOctaves="1" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="18" xChannelSelector="G" yChannelSelector="B"/>
</filter>
</defs>
</svg>
<style>.target { filter: url(#water); }</style>
<script>
const turb = document.querySelector('#water feTurbulence');
let t = 0;
function animate() {
t += 0.02;
const x = 0.015 + 0.0008 * Math.cos(t);
const y = 0.02 + 0.0012 * Math.sin(t * 0.7);
turb.setAttribute('baseFrequency', `${x} ${y}`);
requestAnimationFrame(animate);
}
animate();
</script>


















