真弹性必须用物理模型迭代位置和速度,核心是目标牵引、弹簧力、阻尼衰减;单纯requestAnimationFrame线性插值只能实现缓动,无法模拟震荡、回弹与自然衰减。

Canvas 做弹性效果,不是调个 cubic-bezier 就完事——那只是视觉假象,松手就停。真弹性必须用物理模型迭代位置和速度,核心就三件事:目标牵引、弹簧力、阻尼衰减。
为什么直接改 x/y 不行?
单纯用 requestAnimationFrame 线性插值(比如 x += (targetX - x) * 0.1)会丢掉震荡、回弹、自然衰减这些关键特征。它本质是“缓动”,不是“物理”。真实弹性需要同时维护位置 x、速度 vx 和目标 targetX,每帧按胡克定律 + 阻尼更新速度,再用速度驱动位移。
常见错误现象:
- 松手后球“滑”到目标点,没回弹 → 缺少速度变量或未正确积分
- 球在目标点附近无限抖动 →
damping太小(比如设成0.9而非0.85) - 动画卡顿或跳变 → 用了
setTimeout或未对齐requestAnimationFrame时间步
requestAnimationFrame 里怎么写物理更新?
最简可用的欧拉法更新逻辑长这样(以水平方向为例):
vx += (targetX - x) * k;<br>vx *= d;<br>x += vx;
说明:
-
k是劲度系数,典型值在0.1~0.3之间;值越大,回弹越急,但太大会数值发散 -
d是阻尼系数,范围严格在0~1;0.85比较通用,容易粘滞,> 0.95易震荡 - 顺序不能错:先加力(朝目标加速),再衰减(模拟阻力),最后位移;颠倒会导致能量不守恒
DOM 元素也能做弹性动画吗?
能,但别碰 left/top。它们触发重排,性能差且无法硬件加速。
正确做法:
- 用
transform: translate(x, y)更新位置 - 把
x、vx、targetX存在 JS 对象里,只在渲染阶段写入element.style.transform - 确保元素已提升为合成层(比如加
will-change: transform或transform: translateZ(0))
示例片段:
const ball = { x: 100, y: 100, vx: 0, vy: 0, targetX: 300, targetY: 200 };<br>function animate() {<br> ball.vx += (ball.targetX - ball.x) * 0.15;<br> ball.vy += (ball.targetY - ball.y) * 0.15;<br> ball.vx *= 0.85;<br> ball.vy *= 0.85;<br> ball.x += ball.vx;<br> ball.y += ball.vy;<br> ball.el.style.transform = `translate(${ball.x}px, ${ball.y}px)`;<br> requestAnimationFrame(animate);<br>}
Canvas 画弹簧本身要注意什么?
弹簧不是一条直线,得用分段贝塞尔或正弦扰动生成螺旋线。关键点:
-
segments数量影响平滑度,60~100较稳妥;太少看起来像折线,太多吃性能 - 拉伸时,要动态计算每段的长度和偏移,不能只缩放整条路径
- 固定端坐标(
anchorX/anchorY)和球体当前位置共同决定弹簧总长,用于计算形变量x = length - restLength - 别忘了给球体加高光或阴影,否则弹性感出不来——物理是内核,视觉是出口
真正难的从来不是公式,而是让 k 和 d 在不同设备、不同帧率下都表现稳定。别硬套理论值,多拖几次鼠标,看它回弹几下才停稳,那才是你该信的参数。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











