在 JavaScript 中實現衝突檢測

Sahil Bhosale 2024年2月15日
在 JavaScript 中實現衝突檢測

在 JavaScript 中,當兩個或多個 HTML 元素髮生衝突時,就會發生衝突。預設情況下,當在 HTML 元素上使用移動事件偵聽器時,它不會碰到螢幕上的任何其他元素。

它將通過 DOM 中的其他元素。因此,要在 JavaScript 中檢測衝突,我們必須手動使用元素的 x 和 y 座標進行檢測。

在本文中,我們將在網頁中使用兩個 div 元素(兩個正方形),我們將瞭解如何在其中一個正方形與另一個正方形發生衝突時實現衝突檢測。

在 JavaScript 中實現衝突檢測

我們將純粹在 JavaScript 中實現衝突檢測,我們不會使用任何其他第三方庫。首先,我們將編寫 HTML,然後我們將進入 CSS 部分,最後,我們將編寫 JavaScript 程式碼。

在 HTML 中,我們將新增兩個具有類 square_1square_2div 元素。square_2 將位於固定位置,而 square_1 將在螢幕上的任何位置移動。

<body>
    <div class="square_1"></div>
    <div class="square_2"></div>
</body>

我們將在 CSS 中將寬度和高度等基本樣式新增到正方形的 160px 中,並將兩個正方形的位置更改為 absolute。它將允許我們將方塊移動到螢幕上的任何位置。

我們將給 square_1 一個紅色的背景顏色,給 square_2 一個黃色的背景顏色。

由於我們希望 square_2 固定在螢幕上的特定位置,我們將新增一個 lefttop 屬性並給它一個值 400px。我們還將向 square_1 新增一個 font-size 屬性,因為我們將在與另一個衝突後在其上顯示一個文字。

最後,我們將第一個方格的 z-index 設定為 1,因為我們希望它在移動時保持在另一個方格之上。如果我們不設定此屬性,它將位於第二個正方形下方。

.square_2{
    width: 160px;
    height: 160px;
    position: absolute;
    left: 400px;
    top: 400px;
    background-color:rgb(255, 233, 36);
}

.square_1{
    position: absolute;
    width: 160px;
    height: 160px;
    background-color:rgb(255, 80, 80);
    font-size: 1.5em;
    z-index: 1;
}

輸出:

沒有移動事件監聽器的輸出

我們無法移動紅色方塊,因為我們沒有新增移動事件監聽器。

我們首先必須訪問我們在 HTML 中新增到 JavaScript 程式碼的兩個方塊。為此,我們將使用 getElementsByClassName() 函式來獲取兩個 div 元素並將類名作為引數傳遞給該函式。

由於此函式返回一個陣列,我們將通過索引來獲取第一個元素。然後我們將兩個正方形 div 儲存在 square_1square_2 變數中。

現在我們可以訪問兩個正方形元素,我們將首先獲取這兩個元素的寬度和高度。我們有一個名為 getComputedStyle() 的 JavaScript 函式,它接受一個元素作為引數。

所以,我們將把 square_1square_2 變數作為引數傳遞給這個函式。然後,使用點符號,我們將訪問元素的 CSS 屬性。

請注意,我們還使用了 split() 函式。這是因為 getComputedStyle() 函式返回一個帶有其單位的值。

例如,我們將兩個元素的寬度設定為 160px。所以 getComputedStyle() 函式返回的值將是字串形式的 160px

我們不想要 px 部分,所以我們使用 split() 並在其中傳遞 px。該函式將返回一個包含兩個元素的陣列,第一個元素為 160,第二個元素為空白。

我們需要值本身,所以我們將使用 [0] 來訪問它。

var square_1 = document.getElementsByClassName('square_1')[0];
var square_2 = document.getElementsByClassName('square_2')[0];

var square_1_width =
    Number((getComputedStyle(square_1).width).split('px')[0]) / 2;
var square_2_width =
    Number((getComputedStyle(square_2).width).split('px')[0]) / 2;

var square_1_height =
    Number((getComputedStyle(square_1).height).split('px')[0]) / 2;
var square_2_height =
    Number((getComputedStyle(square_2).height).split('px')[0]) / 2;

由於返回的值將是一個字串,我們將其轉換為一個數字併除以 2,因為我們想要它的一半值,即 80。最後,我們將值儲存到變數中,如上所示。

現在,我們將使用 addEventListener() 函式在 square_1 上新增一個 mousemove 事件偵聽器。如下圖所示,這個函式接受一個事件和一個回撥函式作為引數。

我們將事件 e 傳遞給回撥函式以跟蹤滑鼠在螢幕上的位置。

一旦我們將滑鼠游標懸停在 square_1 上並移動滑鼠,我們希望將滑鼠的左側和頂部位置分配給正方形的左側和頂部位置,以便它可以移動到我們移動滑鼠游標的任何位置。

square_1.addEventListener('mousemove', (e) => {
  square_1.style.left = (e.clientX - square_1_width) + 'px';
  square_1.style.top = (e.clientY - square_1_height) + 'px';

  checkCollision();
});

輸出:

新增了 Mousemove 事件偵聽器衝突

e.clientXe.clientY 給出了滑鼠移動的右上角和右下角位置。

但是,我們希望游標顯示在正方形的中心,因此我們使用 square_1_widthsquare_1_height 減去正方形的寬度和高度,並將半寬和高度儲存在其中。

這將使我們拖動時滑鼠游標指向正方形的中心,如下圖所示。

在執行程式碼之前,請確保註釋掉 checkCollision() 函式,因為我們尚未在程式碼中定義它。

現在,讓我們建立 checkCollision() 函式來檢測衝突。在這裡,我們將首先使用 getComputedStyle() 函式獲取方格的左側和頂部位置。

最後,我們將字串值型別轉換為數字並將其儲存在變數中。

我們將把 square_1 的左側和頂部位置儲存在 x_pos_sq_1y_pos_sq_1 變數中,並將 square_2 儲存在 x_pos_sq_2y_pos_sq_2 變數中。

現在,我們將檢查我們的第一個正方形(紅色)是否穿過第二個正方形(黃色)的四個邊中的任何一個。我們將為第二個正方形(黃色)的每一邊新增一個條件以進行檢查。

請注意,正方形的 x 和 y 位置當前都在正方形的中心。

function checkCollision() {
  let x_pos_sq_1 = Number((getComputedStyle(square_1).left).split('px')[0]);
  let y_pos_sq_1 = Number((getComputedStyle(square_1).top).split('px')[0]);

  let x_pos_sq_2 = Number((getComputedStyle(square_2).left).split('px')[0]);
  let y_pos_sq_2 = Number((getComputedStyle(square_2).top).split('px')[0]);

  let leftPos = x_pos_sq_1 + square_1_width > x_pos_sq_2 - square_2_width;
  let rightPos = x_pos_sq_1 - square_1_width < x_pos_sq_2 + square_2_width;

  let topPos = y_pos_sq_1 + square_1_height > y_pos_sq_2 - square_2_height;
  let bottomPos = y_pos_sq_1 - square_1_height < y_pos_sq_2 + square_2_height;

  if (leftPos && rightPos && topPos && bottomPos) {
    square_1.innerHTML = 'Collision occured';
    square_2.style.border = '5px solid rgb(24, 251, 240)';
  } else {
    square_1.innerHTML = '';
    square_2.style.border = 'none';
  }
}

輸出:

黃色方塊與藍色邊框的衝突

為了檢測紅色方塊是否從左側與黃色方塊發生衝突,我們將檢查紅色方塊的 x 位置和寬度是否大於黃色方塊的 x 位置減去其寬度。如果紅色方塊較大,則從左側與黃色方塊發生衝突。

它將返回一個布林值,我們將儲存在 leftPos 變數中。

為了檢測紅色方塊是否從右側與黃色方塊發生衝突,我們將檢查紅色方塊的 x 位置減去其寬度是否小於黃色方塊的 x 位置及其寬度。如果較小,紅色方塊與右側的黃色方塊發生衝突。

它將返回一個布林值,我們將儲存在 rightPos 變數中。

頂部位置與左側位置相似;唯一的區別是我們處理的是 y 軸和高度,而不是 x 軸和寬度。這將返回一個布林值,我們將其儲存在 topPos 變數中。

底部位置與右側位置類似,但我們處理的是 y 軸和高度。這也將返回一個布林值,我們將其儲存在 bottomPos 變數中。

checkCollision() 函式的末尾,我們將通過新增 leftPosrightPostopPosif 條件來檢查紅色方塊是否越過黃色方塊的邊界。bottomPos 裡面。

如果所有變數都生成一個 true 值,則 square_1square_2 衝突。一旦發生這種情況,我們將為黃色方塊新增藍色邊框,並且我們還將在紅色方塊上新增文字發生衝突,如上所示。

如果我們將紅色方塊移到黃色方塊的邊界之外,那麼我們將從黃色方塊中移除邊框並從紅色方塊中移除文字。

作者: Sahil Bhosale
Sahil Bhosale avatar Sahil Bhosale avatar

Sahil is a full-stack developer who loves to build software. He likes to share his knowledge by writing technical articles and helping clients by working with them as freelance software engineer and technical writer on Upwork.

LinkedIn