碰撞检测(边界检测)在前端游戏,以及涉及拖拽交互的场景应用十分普遍。前端
那么啥叫碰撞?JavaScript 又是如何检测 DOM 发生碰撞的呢?算法
碰撞,顾名思义,就是两个物体碰撞在了一块儿,眼睛是能够直观的观察到碰撞的发生。但对于前端实现,如何让 JavaScript 代码理解两个独立的“物体”(DOM)碰撞在一块儿呢。这就涉及到碰撞检测(或者叫边界检测)的问题了。前端框架
前端常见的的碰撞,咱们能够粗略的分为几下几类:框架
判断任意两个(水平)矩形的任意一边是否有间距,从而得之两个矩形块有没有发生碰撞。具体实现方式,能够选定一个矩形为参照物,计算另外一矩形的与本身相近的边是否发生重合现象。若四边均未发生重合,则未发生碰撞,反之则发生碰撞。dom
图形示例:学习
简单算法实现(非碰撞状况,else 分支就是碰撞状况):优化
if( domA.left > domB.right || domA.top > domB.bottom || domA.right < domB.left || domA.bottom < domB.top ) { return false // 未碰撞 } else { return true // 碰撞 }
判断任意两个圆形碰撞比较简单,只须要判断两个圆的圆心距离是否小于两圆半径之和,若是小于半径和,就能够断定两个圆发生碰撞。spa
图形示例:code
简单算法实现blog
let distance = Math.sqrt(Math.pow(x1 - x2) + Math.pow(y1 - y2) ) if (distance < r1 + r2) { // r一、r2 分别为两圆的半径 return true // 发生碰撞 } else { return false //未发生碰撞 }
圆形与矩形发生碰撞的要点则是要找出矩形上距离圆心最近的点,而后经过判断该点与圆心的决定是否小于圆的半径,若小于则为碰撞。
点的位置,能够经过获取矩形某一个特定的定点坐标结合矩形宽高与圆的圆心进行比较肯定。基本原理与两个矩形碰撞类似,可是略有差别,由于圆形有直接与矩形顶点碰撞的状况。
以下图所示,与矩形碰撞的区别在于,多出了处理矩形宽高延长线交叉区域的状况。当圆心位于两条平行边线之间时:矩形上与圆心最近的点为圆心垂直与矩形最近边线的交叉点;当圆心位于两条交叉边线之间时(以下图斜线区域),与圆心最近的点则是矩形交叉边线的顶点:
简单算法实现(B 圆只是作辅助说明)
// 假设 矩形为 Box, 圆的半径为 R let distance if (x1 < Box.left && y1 > Box.top && y1 < Box.bottom) { // 位于上图中 圆 A 的 位置 distance = Box.left - x1 } else if ( x1 > Box.Right && y1 < Box.top ) { // 位于 上图中 圆 B 的位置 distance = Math.sqrt(Math.pow(x1 - Box.right) + Math.pow(y1 - Box.top)) } // 其余几种状况相似...按着顺/逆时针的顺序,总共八个区域就能全覆盖 if (distance < R) { return true // 碰撞 } else { return false // 未碰撞 }
还有一种涉及到多 DOM 碰撞的状况,通常业务需求是动态生成 DOM,占位,移动,而后再生成新 DOM,使他们不可重叠。经过查资料发现 地图碰撞 算法比较适合这样的场景。
地图碰撞算法 主要是应用了矩阵的思想。即将拖拽区域进行数据化分割,划分红一个假想的数据地图,每个小块算是一个独立的位置格子,经过标记状态来肯定小格子是否被占用。而后应用最简单的 矩形碰撞 来断定拖动的 DOM 是否与格子发生碰撞,发生则将格子状态改成 1,未被占用则标记为 0。只有未被占用的格子能够被占用,占用的格子,释放后标记为 0。这种状态管理的方式,很适合结合 React、Vue 等前端框架作一些拖拽、碰撞的复杂业务交互。
图形示例:
简单思路实现
// 区域是否可用 标记 矩阵 let map = [ [0, 1, 0, 0], [0, 0, 0, 0] ] // 设置初始位置 let NewDom = { left: 0, top: 0} // 判断是否可安置 ...
以上是比较主流的几种碰撞状况,以及主要的思路实现。目前业务有遇到碰撞需求,因此抽时间整理了下。圆形和矩形碰撞的状况感受还能够优化下,若是有好的思路,能够互相学习下。