jQuery/JavaScript 碰撞检测
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4230029/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
jQuery/JavaScript collision detection
提问by Chris Armstrong
How to detect if two <div>
elements have collided?
如何检测两个<div>
元素是否发生碰撞?
The two divs are simple coloured boxes travelling perpendicular to each other, so no complicated shapes or angles.
这两个 div 是相互垂直的简单彩色盒子,因此没有复杂的形状或角度。
采纳答案by ?ime Vidas
var overlaps = (function () {
function getPositions( elem ) {
var pos, width, height;
pos = $( elem ).position();
width = $( elem ).width();
height = $( elem ).height();
return [ [ pos.left, pos.left + width ], [ pos.top, pos.top + height ] ];
}
function comparePositions( p1, p2 ) {
var r1, r2;
r1 = p1[0] < p2[0] ? p1 : p2;
r2 = p1[0] < p2[0] ? p2 : p1;
return r1[1] > r2[0] || r1[0] === r2[0];
}
return function ( a, b ) {
var pos1 = getPositions( a ),
pos2 = getPositions( b );
return comparePositions( pos1[0], pos2[0] ) && comparePositions( pos1[1], pos2[1] );
};
})();
$(function () {
var area = $( '#area' )[0],
box = $( '#box0' )[0],
html;
html = $( area ).children().not( box ).map( function ( i ) {
return '<p>Red box + Box ' + ( i + 1 ) + ' = ' + overlaps( box, this ) + '</p>';
}).get().join( '' );
$( 'body' ).append( html );
});
body {
padding: 30px;
color: #444;
font-family: Arial, sans-serif;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
}
#area {
border: 2px solid gray;
width: 500px;
height: 400px;
position: relative;
}
#area > div {
background-color: rgba(122, 122, 122, 0.3);
position: absolute;
text-align: center;
font-size: 50px;
width: 60px;
height: 60px;
}
#box0 {
background-color: rgba(255, 0, 0, 0.5) !important;
top: 150px;
left: 150px;
}
#box1 {
top: 260px;
left: 50px;
}
#box2 {
top: 110px;
left: 160px;
}
#box3 {
top: 200px;
left: 200px;
}
#box4 {
top: 50px;
left: 400px;
}
p {
margin: 5px 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<h1>Detect overlapping with JavaScript</h1>
<div id="area">
<div id="box0"></div>
<div id="box1">1</div>
<div id="box2">2</div>
<div id="box3">3</div>
<div id="box4">4</div>
</div>
General idea - you get the offset and dimension of the boxes and check whether they overlap.
一般想法 - 您获得框的偏移量和尺寸并检查它们是否重叠。
If you want it to update, you can use setInterval
:
如果您希望它更新,您可以使用setInterval
:
function detectOverlapping() {
// code that detects if the box overlaps with a moving box
setInterval(detectOverlapping, 25);
}
detectOverlapping();
Also, note that you can optimize the function for your specific example.
另请注意,您可以针对特定示例优化该函数。
you don't have to read the box dimensions repeatedly (like I do in my code) since they are fixed. You can read them on page load (into a variable) and then just read the variable
the horizontal position of the little box does not change (unless the user resizes the window). The vertical positions of the car boxes does not change. Therefore, those values also do not have to be read repeatedly, but can also be stored into variables.
you don't have to test whether the little box overlaps with all car boxes at all times. You can - based on its vertical position - figure out in which lane the box is currently, and test only the specific car box from that lane.
您不必重复阅读盒子尺寸(就像我在我的代码中所做的那样),因为它们是固定的。您可以在页面加载时读取它们(到变量中),然后只读取变量
小框的水平位置不会改变(除非用户调整窗口大小)。车箱的垂直位置不变。因此,这些值也不必重复读取,也可以存储到变量中。
您不必始终测试小盒子是否与所有汽车盒子重叠。您可以 - 根据其垂直位置 - 确定盒子当前位于哪个车道,并仅测试该车道上的特定汽车盒子。
回答by Oscar Godson
I believe this is the easiest way: http://plugins.jquery.com/project/collidable
我相信这是最简单的方法:http: //plugins.jquery.com/project/collidable
Here is another one, in German: http://www.48design.de/news/2009/11/20/kollisionsabfrage-per-jquery-plugin-update-v11-8/
这是另一个,德语:http: //www.48design.de/news/2009/11/20/kollisionsabfrage-per-jquery-plugin-update-v11-8/
I'd give those a try.
我会尝试一下。
--UPDATE--
- 更新 -
I can't really spend anytime on it right now, but i can when i get home if no one answers but you;d do something like:
我现在真的不能在上面花任何时间,但是如果我回家后没有人回答你,我可以这样做;我可以做一些类似的事情:
setInterval(function(){
//First step would be to get the offset of item 1 and item 2
//Second would be to get the width of each
//Third would be to check if the offset+width ever overlaps
//the offset+width of the 2nd
//Fourth would be, if so, do X or set a class...
},10);
回答by Murtaza H
Its a little late on this but I guess you could use this approach that I tried when I was faced with the similar situation. The advantage here is that there are no additional plugin, or scripts involved and neither do you have to introduce performance hungry polling into it. This technique uses the the built-in methods and events that Jquery's droppable has to offer.
这有点晚了,但我想你可以使用我在面临类似情况时尝试过的这种方法。这里的优点是不涉及额外的插件或脚本,您也不必在其中引入性能饥渴的轮询。此技术使用 Jquery 的 droppable 必须提供的内置方法和事件。
Ok, enough said, here's the solution technique: Say if you have two elements (images in my case) and you don't want them to overlap or detect when they do, make the two elements a droppable and make them to 'accept' each other:
好吧,说得够多了,这里是解决方案技术:假设你有两个元素(在我的例子中是图像)并且你不希望它们重叠或检测它们何时重叠,使这两个元素成为可放置的并使它们“接受”彼此:
$([div1, div2]).droppable(CONFIG_COLLISSION_PREVENTION_DROPPABLE);
The 'CONFIG_COLLISSION_PREVENTION_DROPPABLE' looks like this:
'CONFIG_COLLISSION_PREVENTION_DROPPABLE' 看起来像这样:
var originatingOffset = null;
CONFIG_COLLISSION_PREVENTION_DROPPABLE = {
tolerance: "touch",
activate : function (event, ui) {
// note the initial position/offset when drag starts
// will be usedful in drop handler to check if the move
// occurred and in cae overlap occurred, restore the original positions.
originatingOffset = ui.offset;
},
drop : function (event, ui) {
// If this callback gets invoked, the overlap has occurred.
// Use this method to either generate a custom event etc.
// Here, i used it to nullify the move and resetting the dragged element's
// position back to it's original position/offset
// (which was captured in the 'activate' handler)
$(ui.draggable).animate({
top: originatingOffset.top + "px",
left: originatingOffset.left + "px"
}, 300);
}
}
The 'activate' and 'drop' handlers refer to the 'dropactivate' and 'drop' events of "droppable" plugin
“activate”和“drop”处理程序指的是“droppable”插件的“dropactivate”和“drop”事件
Here, the key is the 'drop' callback. Whenever any of the two elements overlap and they are dropped over each other, the 'drop' will be called. This is the place to detect and take actions, may be sending out custom events or calling other actions (I here chose to revert the overlapping element's positions to the initial position when the drag started, which was captured in 'activate' callback).
在这里,关键是“drop”回调。每当两个元素中的任何一个重叠并且它们相互放置时,将调用“放置”。这是检测和采取行动的地方,可能是发送自定义事件或调用其他行动(我在这里选择将重叠元素的位置恢复到拖动开始时的初始位置,这是在 'activate' 回调中捕获的)。
That's it. No polling, no plugins, just the built-in events.
就是这样。没有轮询,没有插件,只有内置事件。
Well, there can be other optimizations/extensions done to it, this was simply the first shot out of my head that worked :)
好吧,可以对它进行其他优化/扩展,这只是我头脑中的第一枪:)
You can also use the 'dropover' and 'dropout' events to signal and create a visual feedback to the user that two elements are overlapping, while they may be still on the move.
您还可以使用 'dropover' 和 'dropout' 事件向用户发出信号并向用户创建视觉反馈,即两个元素正在重叠,而它们可能仍在移动中。
var CLASS_INVALID = "invalid";
// .invalid { border: 1px solid red; }
...
$.extend(CONFIG_COLLISSION_PREVENTION_DROPPABLE, {
over : function (event, ui) {
// When an element is over another, it gets detected here;
// while it may still be moved.
// the draggable element becomes 'invalid' and so apply the class here
$(ui.draggable).addClass(CLASS_INVALID);
},
out : function(event, ui) {
// the element has exited the overlapped droppable now
// So element is valid now and so remove the invalid class from it
$(ui.draggable).removeClass(CLASS_INVALID);
}
});
Hope this helps!
希望这可以帮助!
回答by Geke_Sulen
EDIT: I have written a blog post on my website. Here a link to it. http://area36.nl/2014/12/creating-your-own-collision-detection-function-in-javascript/
编辑:我在我的网站上写了一篇博文。这是它的链接。 http://area36.nl/2014/12/creating-your-own-collision-detection-function-in-javascript/
Well I had the same problem but thanks to the answer of Oscar Godson I got a function that works. I used Jquery for easy coding and because i'm lazy ;p. I put the function in a other function that is fired every second so keep that in mind.
好吧,我遇到了同样的问题,但感谢 Oscar Godson 的回答,我得到了一个有效的功能。我使用 Jquery 来轻松编码,因为我很懒;p。我把这个函数放在另一个每秒触发的函数中,所以请记住这一点。
function collidesWith (element1, element2) {
var Element1 = {};
var Element2 = {};
Element1.top = $(element1).offset().top;
Element1.left = $(element1).offset().left;
Element1.right = Number($(element1).offset().left) + Number($(element1).width());
Element1.bottom = Number($(element1).offset().top) + Number($(element1).height());
Element2.top = $(element2).offset().top;
Element2.left = $(element2).offset().left;
Element2.right = Number($(element2).offset().left) + Number($(element2).width());
Element2.bottom = Number($(element2).offset().top) + Number($(element2).height());
if (Element1.right > Element2.left && Element1.left < Element2.right && Element1.top < Element2.bottom && Element1.bottom > Element2.top) {
// Do your stuff here
}
}
What it does is basically it gets all the values of element1
and then get all the values of element2
. Then with the help of some calculations it figures out all the values. Then in the if
statement it compares the square of element1
to the square of element2
. If the values of element1
are between the left, right, top and bottom values of element2
. If that is true the code in the bottom is executed.
它所做的基本上是获取 的所有值,element1
然后获取 的所有值element2
。然后在一些计算的帮助下它计算出所有值。然后在if
语句中,它将 的平方element1
与 的平方进行比较element2
。如果 的值element1
介于 的左侧、右侧、顶部和底部值之间element2
。如果这是真的,则执行底部的代码。
回答by eruciform
I ran into this generalized issue myself, so (full disclosure) I made a plugin for it. For simple collision queries about static objects, try this:
我自己遇到了这个普遍的问题,所以(完全披露)我为它制作了一个插件。对于关于静态对象的简单碰撞查询,试试这个:
http://sourceforge.net/projects/jquerycollision/
http://sourceforge.net/projects/jquerycollision/
Which allows you to get a list of overlapping collision boxes (or none if there's no collision):
这允许您获得重叠碰撞框的列表(如果没有碰撞,则没有):
hits = $("#collider").collision(".obstacles");
hits = $("#collider").collision(".obstacles");
Or to get a collision event during "dragging", use this:
或者要在“拖动”期间获得碰撞事件,请使用:
http://sourceforge.net/apps/mediawiki/jquidragcollide/?source=navbar#collision
http://sourceforge.net/apps/mediawiki/jquidragcollide/?source=navbar#collision
Which gives you a "collision" event to connect to. (Or a "protrusion" event, to see if a div escapes another div that currently contains it.)
这为您提供了要连接的“碰撞”事件。(或“突出”事件,以查看一个 div 是否逃脱了当前包含它的另一个 div。)
$(draggable).bind(
"collision",
function(event,ui) {
...
}
);
If you are checking collisions during motion other than dragging, just call the original repeatedly, it's pretty quick. Note: the dragging one doesn't play nicely with resizing.
如果您在除拖动之外的运动过程中检查碰撞,只需重复调用原件,它非常快。注意:拖动一个不能很好地调整大小。
回答by Anoop B.K
Post is old, May be it help someone...
帖子很旧,可能对某人有帮助...
function CheckDiv()
{
var ediv1 = document.getElementById('DIV1');
var ediv2 = document.getElementById('DIV2');
ediv1.top = $(ediv1).offset().top;
ediv1.left = $(ediv1).offset().left;
ediv1.right = Number($(ediv1).offset().left) + Number($(ediv1).width());
ediv1.bottom = Number($(ediv1).offset().top) + Number($(ediv1).height());
ediv2.top = $(ediv2).offset().top;
ediv2.left = $(ediv2).offset().left;
ediv2.right = Number($(ediv2).offset().left) + Number($(ediv2).width());
ediv2.bottom = Number($(ediv2).offset().top) + Number($(ediv2).height());
if (ediv1.right > ediv2.left && ediv1.left < ediv2.right && ediv1.top < ediv2.bottom && ediv1.bottom > ediv2.top)
{
alert("hi");
}
if (ediv1.left > ediv2.left && ediv1.top > ediv2.top && ediv1.right < ediv2.right && ediv1.bottom < ediv2.bottom)
{
alert("hello");
}
}
回答by Rajat Bhatt
You can do this using getBoundingClientRect()
function isOverlapping(div1, div2){
const div1 = div1.getBoundingClientRect();
const div2 = div2.getBoundingClientRect();
return (div1.right > div2.left &&
div1.left < div2.right &&
div1.bottom > div2.top &&
div1.top < div2.bottom)
}