判断两个<canvas>元素内物体是否碰撞需用JavaScript手动计算几何关系:矩形用AABB检测,圆形用距离公式,混合形状先AABB后精确检测;禁用getBoundingClientRect()和isPointInPath()等不可靠方法,须维护物体数学表示并参与时间步长。

怎么判断两个 <canvas> 元素里的物体是否碰撞了
HTML5 本身不提供“碰撞检测”功能,所有逻辑都得靠 JavaScript 手动计算。真正起作用的是你在 requestAnimationFrame 循环里持续更新的物体位置(x、y、width、height 或 radius),再用几何公式比对——不是靠 DOM 元素或 CSS 属性自动触发。
常见错误是试图监听 collision 事件,或者给 <div> 加 oncollision 属性,这在标准 HTML 中根本不存在。
- 矩形碰撞:检查两个
rect的边界是否重叠(轴对齐包围盒,AABB) - 圆形碰撞:用两点间距离公式,比较
distance <= r1 + r2 - 混合形状(如圆+矩形):通常先用 AABB 快速排除,再做更精确的点-圆或边-圆检测
ctx.isPointInPath() 能不能用来做碰撞检测
不能直接用于物体间碰撞,它只回答“某个坐标点是否落在当前路径内”。你得手动把一个物体的边缘采样成多个点,再逐个调用 isPointInPath() 去测另一个物体——性能差、精度低、逻辑绕。
更实际的做法是:自己维护每个物体的数学表示(比如 {x: 100, y: 150, radius: 20}),用纯数值运算判断,完全绕过渲染上下文。
立即学习“前端免费学习笔记(深入)”;
示例:两个圆碰撞判断
function circlesCollide(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
const distance = Math.sqrt(dx * dx + dy * dy);
return distance <= a.radius + b.radius;
}用 getBoundingClientRect() 检测 DOM 元素碰撞靠谱吗
仅适用于静态、非旋转、非缩放的 <div> 或其他块级元素,且必须确保它们都在同一坐标系(无 transform: translateZ()、iframe 嵌套、position: fixed 等干扰)。一旦有 canvas 绘制内容、CSS 动画、或使用 transform 移动,getBoundingClientRect() 返回的位置就和实际图形位置脱节。
典型陷阱:
- 用
transform: rotate(30deg)旋转一个方块后,它的包围盒变大了,但getBoundingClientRect()返回的是旋转后的外接矩形,不是原始形状 - canvas 内绘制的图形,
getBoundingClientRect()只返回整个<canvas>标签的位置,无法获取内部像素级轮廓 - 滚动页面时,若没用
pageX/pageY校正,坐标会错位
物理引擎要不要引入,比如 Matter.js 或 p2.js
如果你要做带质量、摩擦、弹性、关节、多边形分解的真实物理交互,引入轻量引擎是合理选择;但若只是“小球碰到墙反弹”或“方块走到一起就变色”,手写几十行 AABB + 速度反射就够了。
性能和体积差异明显:
- Matter.js 压缩后约 180KB,启动开销高,调试链路长
- 纯 JS 碰撞逻辑可控制在 1KB 内,所有状态可见、可断点、可单步
- 引擎默认假设你用它的
Body和World,和你已有的 canvas 渲染循环容易耦合过深
建议先实现最简版本,等出现以下情况再考虑升级:需要连续碰撞(tunneling)、旋转体精确碰撞、或频繁添加/销毁动态刚体。
最容易被忽略的一点:无论手写还是用引擎,**时间步长(delta time)必须参与速度计算**。固定帧率(如 60fps)下用 dx = vx 还能凑合,但只要动画卡顿或设备性能波动,没乘 delta 就会漂移、穿透、反弹失真。











