HTML怎么做canvas碰撞检测_HTML canvas碰撞检测算法方法全面解析

作者:袖梨 2026-07-25
应使用AABB算法进行Canvas矩形碰撞检测,而非getBoundingClientRect();因后者受transform、滚动、iframe影响易偏移,需手动校正且易出错,而AABB直接基于Canvas坐标系的x、y等属性计算,稳定可靠。

矩形碰撞检测用 AABB 判断,别直接比 getBoundingClientRect()

Canvas 里做矩形碰撞,最常用也最稳的是轴对齐边界框(AABB)算法,不是靠 getBoundingClientRect() 拿 DOM 坐标——那玩意儿快是快,但一遇上 transform、滚动、iframe 就偏移失真,得手动校正,反而容易出错。

你该直接用 Canvas 坐标系下的对象属性:每个矩形有 xy、<code>height,判断逻辑就一行:

function isRectCollision(a, b) {  return !(a.x + a.width <= b.x || b.x + b.width <= a.x ||           a.y + a.height <= b.y || b.y + b.height <= a.y);}
  • 这个写法本质是“找分离轴”:只要水平或垂直方向完全分离,就不碰撞
  • 避免用 a.x 这类正向条件,容易漏掉等号边界(比如刚好贴边时误判)
  • 所有坐标必须统一在 Canvas 画布坐标系下,别混用 CSS 像素或视口坐标

圆形碰撞别调 Math.hypot(),手算 dx*dx + dy*dy 更安全

两个圆是否碰撞,只看圆心距离是否小于半径和。但 Math.hypot(dx, dy) 在旧版 Safari 和部分 Electron 环境里不支持,而且开方本身有浮点误差和性能开销。

直接平方比较,既快又稳:

立即学习“前端免费学习笔记(深入)”;

function isCircleCollision(a, b) {  const dx = a.x - b.x;  const dy = a.y - b.y;  const rSum = a.radius + b.radius;  return dx * dx + dy * dy < rSum * rSum;}
  • 注意:这里用的是 < 而不是 <=,因为“刚好相切”通常不算碰撞(除非你的游戏逻辑明确需要)
  • 如果对象带旋转,radius 不能直接取渲染尺寸——得用原始定义半径,否则缩放后会误判
  • 圆形适合子弹、球体、头像徽章这类中心对称物体,不适合长条状角色

矩形和圆形混合碰撞,关键在找“矩形上离圆心最近的点”

比如小球撞墙、角色撞箱子,不能套用纯矩形或纯圆形逻辑。核心是:先求圆心在矩形边界上的投影点(即“最近点”),再测该点到圆心的距离是否 ≤ 半径。

function isAABBCircleCollision(rect, circle) {  const clampedX = Math.max(rect.x, Math.min(circle.x, rect.x + rect.width));  const clampedY = Math.max(rect.y, Math.min(circle.y, rect.y + rect.height));  const dx = circle.x - clampedX;  const dy = circle.y - clampedY;  return dx * dx + dy * dy <= circle.radius * circle.radius;}
  • clampedX/Y 是把圆心 x/y “拉回”矩形范围的结果:在内就是圆心本身,在外就是对应边上的垂足
  • 这个算法天然覆盖三种情况:圆心在矩形内、在边上、在角外——不用分 case 写
  • 别忘了矩形坐标必须是 Canvas 坐标系下的,且未被 scale()translate() 影响;如果用了 canvas 变换,得先反向变换圆心坐标

像素级碰撞要防跨域,且只读交集区域

当你要检测带透明边缘的角色图、不规则地形贴图是否真正“像素相触”,就得走 getImageData()。但它有两个硬限制:一是跨域图片会触发 canvas 污染(SecurityError),二是全图读取太慢。

  • 解决跨域:服务端加 CORS 头,或前端加载时设 img.crossOrigin = 'anonymous'
  • 别用 ctx.getImageData(0, 0, canvas.width, canvas.height) —— 一次读几 MB 图像数据,帧率直接崩
  • 只读两物体包围盒的交集区域:ctx.getImageData(x, y, width, height),其中 x/y/width/height 是交集矩形的 Canvas 坐标
  • 遍历时跳过 alpha === 0 的像素,只比非透明通道;若两个对象在交集区都有非透明像素,才算碰撞

这一步几乎总是性能瓶颈,上线前务必用 console.time() 测真实耗时——尤其在低端 Android 设备上,10×10 像素交集都可能卡顿。

相关文章

精彩推荐