香草 JS Div 碰撞检测
Vanilla JS Div Collision Detection
我的以下实现可以在 jsfiddle.net
上找到
我有四个 div。我的目标是让它们可以在页面上拖动,但不允许它们相互重叠。每个都可以使用鼠标移动侦听器在页面上拖动。
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`;
}
}
严格来说,我的碰撞检测功能有效。我将 "Collision" 事件输出到 div,这样当你拖动它时,你可以在 fiddle 中看到它。但是,您仍然可以将 div 相互拖动。
当您尝试将它们拉开时,它们有点 "stick",如果您继续推动它们,它们就会重叠。碰撞检测此时在 true/false 之间摇摆不定,所以我猜这里可能发生了一些奇怪的事情,但我无法弄清楚。
我认为一个问题可能是碰撞检测仅在边界相等时才输出碰撞。也就是说,一旦发生碰撞并且一个元素在另一个元素中,它 returns false。
但是,我看不出我的 mousemove e.movementX 和 e.movementY 事件如何能够通过碰撞测试并移动 div。
这比看起来要复杂一些。
本质上,您需要做的是获取两组坐标,即当前(移动)元素的坐标和与之发生碰撞的坐标。
一旦检测到碰撞,找出哪个轴的差异最小,然后捕捉这些坐标。
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';
}
}
}
这应该可以解决您的问题...
您将有不止 1 个物体发生碰撞。脚本会给您所有的碰撞。但是 accept/move 的逻辑我猜取决于你想要实现的目标。借自 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/>`;
});
}
我的以下实现可以在 jsfiddle.net
上找到我有四个 div。我的目标是让它们可以在页面上拖动,但不允许它们相互重叠。每个都可以使用鼠标移动侦听器在页面上拖动。
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`;
}
}
严格来说,我的碰撞检测功能有效。我将 "Collision" 事件输出到 div,这样当你拖动它时,你可以在 fiddle 中看到它。但是,您仍然可以将 div 相互拖动。
当您尝试将它们拉开时,它们有点 "stick",如果您继续推动它们,它们就会重叠。碰撞检测此时在 true/false 之间摇摆不定,所以我猜这里可能发生了一些奇怪的事情,但我无法弄清楚。
我认为一个问题可能是碰撞检测仅在边界相等时才输出碰撞。也就是说,一旦发生碰撞并且一个元素在另一个元素中,它 returns false。
但是,我看不出我的 mousemove e.movementX 和 e.movementY 事件如何能够通过碰撞测试并移动 div。
这比看起来要复杂一些。
本质上,您需要做的是获取两组坐标,即当前(移动)元素的坐标和与之发生碰撞的坐标。 一旦检测到碰撞,找出哪个轴的差异最小,然后捕捉这些坐标。
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';
}
}
}
这应该可以解决您的问题...
您将有不止 1 个物体发生碰撞。脚本会给您所有的碰撞。但是 accept/move 的逻辑我猜取决于你想要实现的目标。借自 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/>`;
});
}