水波纹进度特效是液位填充式进度指示器,非点击反馈水波浪动画;SVG实现最稳,用封闭波浪路径+clipPath或transform控制高度;Canvas适合高动态场景但需适配像素比、清空画布及预留液面系数。

水波纹进度特效 ≠ 水波浪动画,别混用
仪表盘里要的不是「水面晃动」效果,而是带水波纹理的**液位填充式进度指示器**(比如电量、水位、健康值)。它本质是「波浪形液面 + 进度驱动高度变化」,不是按钮点击时那个扩散圆。用 button::after + @keyframes ripple 做出来的是点击反馈,填不满容器,也映射不了 0%–100% 进度——直接套用会白忙活。
SVG 实现最稳,兼容性好且易控进度
核心是用 <svg></svg> 容器画一个封闭波浪路径,再用 <clippath></clippath> 或 transform: translateY() 控制液面高度。不推荐纯 CSS 伪元素叠波浪,因为:无法响应式缩放、Safari 对 clip-path 百分比支持差、波形变形不可控。
-
<svg viewbox="0 0 200 200"></svg>是基准,所有坐标按此归一化,避免缩放失真 - 波浪路径用两段三次贝塞尔曲线拼接,
d属性中控制周期(建议stroke-dasharray: 120) - 液面 y 坐标 =
100 - (progress * 80)(0% 在顶部,100% 到底部,留出 20px 边距) - 用 JS 动态更新
<path></path>的d和<g></g>的transform,不要靠 CSSanimation驱动液位
Canvas 方案适合高动态场景,但得防坑
如果仪表盘要同时渲染十几个水球、或需实时响应传感器数据(如每 100ms 更新一次),Canvas 更合适。但它不像 SVG 那样声明式,必须手动管理重绘和像素比。
- 初始化时设
canvas.width = canvas.offsetWidth * window.devicePixelRatio,否则高清屏模糊 - 波浪公式必须带振幅:
y = centerY + amplitude * Math.sin(x * frequency + time * speed),漏乘amplitude就是条直线 - 每次
draw()前必调ctx.clearRect(0, 0, canvas.width, canvas.height),否则残影堆积 - 液面高度计算要预留系数:
liquidY = centerY + (1 - progress) * radius * 0.9,不然满值时水溢出球体
别忽略父容器的定位与溢出控制
无论 SVG 还是 Canvas,只要水波区域超出容器边界,就会被截断或撑开布局。常见错误是忘了设 overflow: hidden 或父级没 position: relative。
立即学习“前端免费学习笔记(深入)”;
- SVG 容器外层加
style="overflow: hidden; position: relative;" - Canvas 必须包裹在
<div style="position: relative; overflow: hidden;"> 里<li>如果仪表盘用 Flex/Grid 布局,确保该容器有明确宽高,不能依赖内容撑开</li> <p>真实项目里最容易卡住的,是把「点击水波纹」和「进度水波纹」当成一回事去查文档;其次是 Canvas 初始化时没适配 <code>devicePixelRatio,导致在 MacBook 或 iPhone 上看起来糊成一片。



















