重力效果核心是每帧更新速度vy += g,g取0.2~0.5(推荐0.3),需配合位置校正(y = canvas.height - radius)与反弹衰减(damping 0.85~0.92),并用requestanimationframe驱动,碰撞检测应避免开根号。

Canvas 里加个重力效果,本质不是“画出来”,而是每帧算出小球该在哪、速度该多快——vy += g 这一行代码才是核心,其余都是围绕它做位置修正和能量衰减。
重力加速度怎么设才自然
重力在 Canvas 坐标系里是向下加速,所以 g 必须是正数(比如 0.2~0.5),别写成负值。设太大,小球下坠像砸地板;太小,看起来像在月球上飘。
-
g = 0.3是多数 demo 的起点值,适合中等尺寸小球(半径 8~15px)和 60fps 动画 - 如果 canvas 高度大(比如全屏),可适当提高到
0.45,否则下落时间过长,动画显得迟滞 - 不要用固定
setTimeout模拟帧率;必须用requestAnimationFrame,否则加速度叠加会因帧间隔抖动而失真
触地反弹总卡进地板?位置校正必须做
只改速度不调位置,小球下一帧还会被判定“仍在地下”,导致 vy 反复翻转、数值崩坏甚至卡死。
- 检测条件用
y + radius >= canvas.height,不是y >= canvas.height - 校正语句必须紧跟在判断之后:
y = canvas.height - radius - 反弹衰减系数
damping推荐0.85~0.92:低于0.7显得僵硬,高于0.95容易“跳个没完” - 左右边界同理,但注意:
x - radius 时校正为 <code>x = radius,x + radius >= canvas.width时校正为x = canvas.width - radius
多个小球碰撞时,距离判断别漏开根号
两个小球中心距离小于半径和才算碰撞,但直接算 Math.sqrt(dx*dx + dy*dy) 性能差——应该用平方比较。
- 正确写法:
dx*dx + dy*dy - 错误写法:
Math.sqrt(...) (每帧多一次开方,60 个小球就要上千次) - 碰撞后先分离再更新速度:沿连线方向把两球轻轻推开(比如各退
0.5 * overlap),否则下一帧又触发,vx/vy越叠越大 - 若只要视觉效果、不追求物理精确,可跳过动量计算,直接交换
vx和vy分量(仅限等质量小球)
canvas尺寸变化后小球乱飞?重置逻辑要覆盖所有状态
窗口缩放时只重设 canvas 宽高,但没清空或重生成小球数组,旧坐标可能超出新边界,导致瞬间飞出屏幕或撞穿地面。
-
window.onresize中除了canvas.width/height,必须调用重初始化函数(如chushi()) - 重初始化时,不能只
arr.length = 0,还要确保每个新小球的y初始值不在画布外(比如避免y = -h/2在缩放后变成超大负值) - 推荐给小球加个安全区约束:
y = Math.max(-50, Math.min(h + 50, y)),防止初始位置极端异常
最容易被忽略的是:重力模拟的稳定性极度依赖帧间时间一致性。哪怕只是某几帧卡顿,vy 累加就会偏移,后续所有反弹节奏全乱——所以别在动画循环里塞 console.log 或 DOM 查询操作。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











