【问题标题】:Vanilla JS Div Collision DetectionVanilla JS Div 碰撞检测
【发布时间】:2017-01-08 23:08:33
【问题描述】:

我的以下实现可以在jsfiddle.net上找到

我有四个 div。我的目标是让它们在页面上可拖动,但不允许它们相互重叠。每个都可以使用 mousemove 侦听器在页面上拖动。

container.addEventListener('mousemove',mouseMove);
  function mouseMove(e) {
    if (!mouseDown) {return;}
    let coords=e.target.getBoundingClientRect();
    let movX=e.movementX;
    let movY=e.movementY;
    if (!collision(movX,movY,e.target.classList[1],coords)){
      e.target.style.left=`${coords.left+movX}px`;
      e.target.style.top=`${coords.top+movY}px`;
    }
  }

严格来说,我的碰撞检测功能有效。我将“碰撞”事件输出到一个 div,这样当你拖动它时你可以在小提琴中看到它。但是,您仍然可以将 div 拖到另一个之上。

当您尝试将它们拉开时,它们会有点“粘”,如果您继续推动它们,它们会重叠。在这一点上,碰撞检测在真/假之间非常迅速地摇摆不定,所以我猜这里可能发生了一些奇怪的事情,但我无法弄清楚。

我认为一个问题可能是碰撞检测仅在边界相等时才输出碰撞。也就是说,一旦发生碰撞并且一个元素在另一个元素内,它就会返回 false。

但是,我看不到我的 mousemove e.movementX 和 e.movementY 事件如何通过碰撞测试并移动 div。

【问题讨论】:

  • 在您的示例中,您的“项目”是方形的。会一直这样吗?
  • 它们也可以是矩形的。
  • 我在下面给出的答案处理矩形,只要它们的大小都相同。如果没有,你可能需要摆弄一下......

标签: javascript css collision detection


【解决方案1】:

您将让对象与不止 1 个发生碰撞。脚本将为您提供所有碰撞。但是我猜是否接受/移动它的逻辑取决于你想要实现的目标。借自intersects

脚本:

function mouseMove(e) {
  if (!mouseDown) {
    return;
  }
  let coords = e.target.getBoundingClientRect();
  let movX = e.movementX;
  let movY = e.movementY;

  collision(movX, movY, e.target.classList[1], coords) //check all collisions. Item can collide with more than one polygon.

  e.target.style.left = `${coords.left+movX}px`;
  e.target.style.top = `${coords.top+movY}px`;
  /* if (!) {

  }*/
}

function collision(newX, newY, movingPart, movingRect) {
  let takenPositions = []; //array of arrays of rects' L, R, Top, Bottom coords
  let newCoords = {
    id: movingPart,
    width: 100,
    height: 100,
    x: movingRect.left + newX,
    y: movingRect.top + newY
  };

  let collision = false;
  let collisions = []; //store collisions. 
  divs.forEach((d) => {
    if (d.classList[1] !== movingPart) { // a thing can't collide with itself
      let c = d.getBoundingClientRect();
      takenPositions.push({
        id: d.classList[1],
        width: 100,
        height: 100,
         x: c.left,//updated this part x,y are undefined :|
         y: c.top //and updated this
      });
    }
  });

  takenPositions.forEach((p) => {
    var tw = p.width;
    var th = p.height;
    var rw = newCoords.width;
    var rh = newCoords.height;
    if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) {
      collision = false;
    } else {
      var tx = p.x;
      var ty = p.y;
      var rx = newCoords.x;
      var ry = newCoords.y;
      rw += rx;
      rh += ry;
      tw += tx;
      th += ty;
      collision = ((rw < rx || rw > tx) && (rh < ry || rh > ty) && (tw < tx || tw > rx) && (th < ty || th > ry));
      collisions.push({
        parentId: newCoords.id,
        destId: p.id,
        collision: collision
      });
    }
  });
  let info = document.querySelector('div.info');
  info.innerHTML = "";
  collisions.forEach(function(element) {
    info.innerHTML += `${element.parentId} collision with ${element.destId} is ${element.collision}.  <br/>`;
  });
}

【讨论】:

  • 感谢您提供相交的链接。我现在可能非常密集,但由于某种原因,代码没有记录元素之间的任何冲突:fiddle
  • @thomasdanner 抱歉,我更新了代码。 takenPosition x 和 y 值应该是 x: c.left, y:c.top。我只是在小提琴中尝试过。让我知道..
【解决方案2】:

这比看起来要复杂一些。

基本上,您需要做的是获取两组坐标,即当前(移动)元素的坐标集,以及与它发生碰撞的坐标集。 一旦检测到碰撞,找出哪个轴的差异最小,然后捕捉这些坐标。

var ac = a.getBoundingClientRect(); // coordinates for element 'a'
var bc = b.getBoundingClientRect(); // and 'b'

// assuming both boxes are same size...
// if not, use your existing collision code.

if(Math.abs(ac.top - bc.top) < ac.height && Math.abs(ac.left - bc.left) < ac.width) {
// collision here...

    if(Math.abs(ac.top - bc.top) < Math.abs(ac.left - bc.left)) {
    // vartical offset is smaller, so snap 'y's

        if(ac.top < bc.top) { // a is above b, so snap a's bottom to b's top
            a.style.top = bc.top - ac.height - 1 + 'px';
        }
        else {
            a.style.top = bc.top + bc.height + 1 + 'px';
        }

    }
    else { // here, horizontal offset is smaller, so snap 'x's

        if(ac.left < bc.left) { // a is to the left of b, so snap a's right to b's left
            a.style.left = bc.left - ac.width - 1 + 'px';
        }
        else {
            a.style.left = bc.left + bc.width + 1 + 'px';
        }

    }

}

这应该可以解决您的问题...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-09
    • 2011-10-06
    • 2013-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多