簡析 js 碰撞檢測原理與算法實現

碰撞檢測(邊界檢測)在前端遊戲,以及涉及拖拽交互的場景應用十分普遍。前端

那麼啥叫碰撞?JavaScript 又是如何檢測 DOM 發生碰撞的呢?算法

碰撞,顧名思義,就是兩個物體碰撞在了一塊兒,眼睛是能夠直觀的觀察到碰撞的發生。但對於前端實現,如何讓 JavaScript 代碼理解兩個獨立的「物體」(DOM)碰撞在一塊兒呢。這就涉及到碰撞檢測(或者叫邊界檢測)的問題了。前端框架

碰撞檢測的常見需求場景:

前端常見的的碰撞,咱們能夠粗略的分爲幾下幾類:框架

兩個矩形塊的碰撞:

判斷任意兩個(水平)矩形的任意一邊是否有間距,從而得之兩個矩形塊有沒有發生碰撞。具體實現方式,能夠選定一個矩形爲參照物,計算另外一矩形的與本身相近的邊是否發生重合現象。若四邊均未發生重合,則未發生碰撞,反之則發生碰撞。dom

圖形示例:學習

clipboard.png

簡單算法實現(非碰撞狀況,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

clipboard.png

簡單算法實現blog

let distance = Math.sqrt(Math.pow(x1 - x2) + Math.pow(y1 - y2) )
    if (distance < r1 + r2) { // r一、r2 分別爲兩圓的半徑
        return true // 發生碰撞
    } else {
        return false //未發生碰撞
    }

圓形與矩形塊的碰撞:

圓形與矩形發生碰撞的要點則是要找出矩形上距離圓心最近的點,而後經過判斷該點與圓心的決定是否小於圓的半徑,若小於則爲碰撞。

點的位置,能夠經過獲取矩形某一個特定的定點座標結合矩形寬高與圓的圓心進行比較肯定。基本原理與兩個矩形碰撞類似,可是略有差別,由於圓形有直接與矩形頂點碰撞的狀況。

以下圖所示,與矩形碰撞的區別在於,多出了處理矩形寬高延長線交叉區域的狀況。當圓心位於兩條平行邊線之間時:矩形上與圓心最近的點爲圓心垂直與矩形最近邊線的交叉點;當圓心位於兩條交叉邊線之間時(以下圖斜線區域),與圓心最近的點則是矩形交叉邊線的頂點:

clipboard.png

簡單算法實現(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 等前端框架作一些拖拽、碰撞的複雜業務交互。

圖形示例:

clipboard.png

簡單思路實現

// 區域是否可用 標記 矩陣
 let map = [
        [0, 1, 0, 0],
        [0, 0, 0, 0]
    ]
 // 設置初始位置
 let NewDom = { left: 0, top: 0}
 // 判斷是否可安置
 ...

以上是比較主流的幾種碰撞狀況,以及主要的思路實現。目前業務有遇到碰撞需求,因此抽時間整理了下。圓形和矩形碰撞的狀況感受還能夠優化下,若是有好的思路,能夠互相學習下。

相關文章
相關標籤/搜索