HTML5 Canvas 水波效果靠手动模拟位移映射,核心是 requestAnimationFrame + getImageData/putImageData 实时逐像素正弦偏移,关键三步:控制幅度(2–8px)、频率、相位推进速度。

HTML5 Canvas 实现水波荡漾效果靠的是什么
不是 filter CSS 属性,也不是 WebGPU 或 WebGL 高级 API —— 纯 HTML5 水波效果几乎全靠 Canvas 2D 手动模拟位移映射(displacement map),核心是逐像素计算正弦扰动偏移量。
常见误区:以为加个 filter: url(#water) 就能出水波。SVG 滤镜虽支持 ,但浏览器对动态噪声 + 动画的支持极差,且无法响应鼠标/触摸交互。
真正可用的路径只有一条:用 requestAnimationFrame + getImageData/putImageData 做实时像素重采样。
Canvas 水波滤镜的关键三步怎么写
水波本质是「把原图每个像素的采样坐标,按正弦函数横向/纵向偏移」。实现时必须控制好三个变量:扰动幅度、频率、相位推进速度。
立即学习“前端免费学习笔记(深入)”;
-
amplitude(幅度)设太大图像撕裂,建议 2–8 像素;设太小( -
frequency(频率)决定波纹疏密,0.01–0.03较自然;值越大越像高频抖动而非水波 -
phase(相位)每帧递增,如phase += 0.02;过快会晕眩,过慢像卡顿
示例片段(简化版):
const ctx = canvas.getContext('2d');
const imgData = ctx.getImageData(0, 0, w, h);
const data = imgData.data;
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
const offsetX = Math.sin(x freq + phase) amplitude;
const offsetY = Math.cos(y freq + phase 0.7) amplitude;
const srcX = Math.max(0, Math.min(w - 1, x + offsetX));
const srcY = Math.max(0, Math.min(h - 1, y + offsetY));
const srcIdx = (srcY w + srcX) 4;
const dstIdx = (y w + x) * 4;
data[dstIdx] = data[srcIdx]; // R
data[dstIdx + 1] = data[srcIdx + 1]; // G
data[dstIdx + 2] = data[srcIdx + 2]; // B
data[dstIdx + 3] = data[srcIdx + 3]; // A
}
}
ctx.putImageData(imgData, 0, 0);
为什么直接用 getImageData 会卡顿甚至崩溃
因为每次读写都是全图内存拷贝,1920×1080 图像单次操作就要处理约 800 万像素,CPU 压力极大。Chrome 下连续调用 getImageData 容易触发 DOMException: Failed to execute 'getImageData' on 'CanvasRenderingContext2D' 错误。
缓解方法:
- 缩小绘制区域:只对局部区域(如鼠标 hover 区域)应用水波,用
canvas.width/height控制画布尺寸,而非撑满屏幕 - 降帧率:不用
requestAnimationFrame满帧,改用setTimeout(..., 60)锁 16ms 以上间隔 - 避免重复创建
ImageData:复用同一块内存,用ctx.createImageData(w, h)预分配 - 禁用抗锯齿:
ctx.imageSmoothingEnabled = false,防止缩放采样额外开销
有没有更轻量的替代方案
有,但要接受效果妥协。如果只是需要「看起来像水波」而非物理模拟,可考虑:
- CSS
backdrop-filter: blur(2px)+ 微动画transform: translateX(1px)模拟晃动感(仅适用于背景透明容器) - 用
播放预渲染的水波遮罩视频(MP4/WebM),叠加在目标元素上,opacity 设为 0.15–0.3 - Three.js 加载简单水面 Shader(如
MeshStandardMaterial配normalMap),但已超出纯 HTML5 范畴
真正零依赖、可交互、跨浏览器稳定的水波,还是得回到 Canvas 像素循环 —— 只是别忘了把幅度、频率、相位这三个数调到人眼舒服的区间,否则再准的算法也像故障显示器。










