核心是用requestanimationframe驱动精准倒计时与轻量水波渲染:倒计时逻辑独立于帧率,基于performance.now()计算真实流逝时间;水波用canvas正弦路径模拟,每帧仅更新相位与衰减振幅;页面隐藏时监听visibilitychange补时差,振幅趋近0时停绘波纹只留数字淡出。

用 requestAnimationFrame 制作倒计时水波纹动效,核心不是“画水波”,而是用它驱动**时间精准、渲染流畅、资源省电**的动画循环——水波纹只是视觉表现层,倒计时逻辑必须与帧节奏解耦,否则会漂移或卡顿。
倒计时逻辑要独立于帧循环
很多人误把 setInterval 换成 requestAnimationFrame 就算“优化”,其实大错。RAF 只控制渲染时机,不保证每秒60次;而倒计时必须按真实时间推进。
- 记录开始时间(
Date.now()或performance.now()),每次 RAF 回调中计算已过毫秒数 - 用剩余毫秒数推算当前秒/毫秒值,再转成倒计时显示(如
mm:ss.SSS) - 避免在 RAF 里做
setTimeout或递归setTimeout,那会引入双重定时器抖动
水波纹动效用 Canvas + 正弦波模拟
真物理水波开销大,网页级用“假波纹”更高效:在 canvas 上用 ctx.beginPath() + ctx.bezierCurveTo() 或正弦路径描边,每帧只更新振幅和相位。
- 定义波形参数:周期(如 200px)、振幅(随倒计时减小)、相位偏移(用
time * 0.01动态推进) - 用
Math.sin(x * freq + phase)算每个 x 对应的 y 偏移,拼接为闭合路径(底部平直 + 波形顶部) - 填充渐变色(如从透明到蓝色),并叠加一层半透明白色高光模拟反光,增强水感
RAF 循环里只做三件事
每一帧必须轻量:计算 → 绘制 → 下一帧。任何 DOM 查询、样式重排、复杂运算都放外面。
- 计算:用当前时间戳算倒计时数值 + 波纹相位 + 振幅衰减(如
amp = baseAmp * (remainingMs / totalMs)) - 绘制:清空 canvas → 画背景 → 画波纹路径 → 填充 → 画数字文本(用
ctx.fillText(),字体加阴影提升可读性) - 下一帧:
requestAnimationFrame(animate)放在函数末尾,别包在 if 或 setTimeout 里
防掉帧与自动暂停技巧
用户切走标签页时,RAF 自动暂停,但倒计时不能停——得监听页面可见性变化,手动补时差。
- 用
document.hidden和visibilitychange事件判断页面是否激活 - 隐藏时记下
lastTime;显示时立刻计算差值,修正倒计时剩余时间,再继续 RAF - 给 canvas 加
will-change: transform(仅当需后续做 CSS 缩放时),触发 GPU 加速;但别滥用
不复杂但容易忽略:水波纹的“动”是心理暗示,振幅降到 1px 以下就停绘波纹,只留数字淡出,反而更显结束感。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











