水滴扩散效果必须用canvas 2d实现,因css无法模拟波纹传播物理感;点击坐标需用getboundingclientrect校准;动画须用requestanimationframe;每个水滴需独立生命周期管理;draw前必clearrect;半径用固定增量增长;需限制水滴数量防性能瓶颈。

水滴扩散效果必须用 Canvas 2D 上下文,不能靠 CSS 动画模拟
Canvas 是唯一能实时绘制逐帧波纹衰减、透明度渐变和圆形扩散轨迹的方案。CSS transform + opacity 只能做单次缩放淡出,无法模拟水波向外传播+中心凹陷+边缘扰动的物理感。真实水滴效果依赖像素级控制,必须调用 ctx.beginPath()、ctx.arc() 和 ctx.globalAlpha 动态更新。
点击坐标要转成 Canvas 像素坐标,别直接用 event.clientX
Canvas 元素可能被缩放、有边框或外边距,event.clientX 返回的是视口坐标,不等于画布内部坐标。必须用 getBoundingClientRect() 校准:
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// 后续用 x/y 作为波纹圆心
});
- 漏掉
rect.left/top会导致水滴总出现在左上角 - 如果 Canvas 设置了
width/height属性(非 CSS),还要除以rect.width / canvas.width做 DPI 缩放适配 - 移动端需监听
touchstart并取touches[0]
扩散动画要用 requestAnimationFrame 控制帧率,别用 setInterval
setInterval 无法同步屏幕刷新节奏,容易卡顿或跳帧;requestAnimationFrame 才能保证每帧渲染稳定在 60fps。每个水滴应维护独立生命周期对象,含当前半径、透明度、衰减速度:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
const ripple = { x, y, radius: 0, alpha: 1, decay: 0.05 };
ripples.push(ripple);
<p>function animate() {
ripples.forEach((r, i) => {
r.radius += 3; // 扩张速度
r.alpha -= r.decay;
if (r.alpha </p>
- 每次
draw()前必须调用ctx.clearRect(0, 0, canvas.width, canvas.height) - 半径增长用固定增量(如
+= 3),不是乘法,否则扩散会越来越快 - 多个水滴叠加时,
globalAlpha要在每次arc()前设置,否则影响后续绘制
性能瓶颈在高频点击——必须限制同时存在的水滴数量
连续猛点会让 ripples 数组暴涨,每帧遍历+绘制拖慢主线程。建议硬性上限(如 10 个)并优先淘汰最老的:
if (ripples.length >= 10) {
ripples.shift(); // 删除最早一个
}
- 不限制时,1 秒点 20 下,1 秒后就有 20×60=1200 次冗余绘制
- 用
Array.shift()比splice(0,1)略快,但更关键的是避免 push 前不检查 - 如果要做「水滴碰撞融合」等高级效果,得改用空间分区(quadtree)优化,但基础版没必要
真正难的不是画一个圈,而是让几十个圈互不干扰地呼吸、消散、叠加——Canvas 帧率和数组管理比数学公式更早成为瓶颈。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










