Обнаружение коллизий jQuery/JavaScript
Как определить, если два <div>
элементы столкнулись?
Эти два элемента представляют собой простые цветные прямоугольники, движущиеся перпендикулярно друг другу, поэтому никаких сложных форм или углов.
6 ответов
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>
Общая идея - вы получаете смещение и размеры блоков и проверяете, перекрываются ли они.
Если вы хотите обновить его, вы можете использовать setInterval
:
function detectOverlapping() {
// code that detects if the box overlaps with a moving box
setInterval(detectOverlapping, 25);
}
detectOverlapping();
Также обратите внимание, что вы можете оптимизировать функцию для вашего конкретного примера.
вам не нужно многократно читать размеры блока (как я это делаю в своем коде), поскольку они исправлены. Вы можете прочитать их при загрузке страницы (в переменную), а затем просто прочитать переменную
горизонтальное положение маленькой коробки не изменяется (если пользователь не изменяет размеры окна). Вертикальные положения автомобильных коробок не меняются. Следовательно, эти значения также не должны считываться повторно, но также могут храниться в переменных.
Вам не нужно проверять, перекрывает ли маленькая коробочка все автомобильные ящики. Вы можете - в зависимости от его вертикального положения - выяснить, в какой полосе находится данный бокс, и протестировать только конкретный автомобильный бокс из этой полосы.
Я считаю, что это самый простой способ: http://plugins.jquery.com/project/collidable
Вот еще один, на немецком языке: http://www.48design.de/news/2009/11/20/kollisionsabfrage-per-jquery-plugin-update-v11-8/
Я бы дал этим попробовать.
--ОБНОВИТЬ--
Я не могу сейчас тратить время на это, но я могу, когда вернусь домой, если никто не ответит, кроме вас;
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);
Вы можете сделать это с помощью 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)
}
Это немного поздно, но я думаю, вы могли бы использовать этот подход, который я попробовал, когда столкнулся с подобной ситуацией. Преимущество здесь в том, что в нем нет дополнительных плагинов или сценариев, и вам не нужно вводить в него опрос с высокой производительностью. Этот метод использует встроенные методы и события, которые может предложить Jquery.
Хорошо, достаточно сказано, вот метод решения: скажем, если у вас есть два элемента (изображения в моем случае), и вы не хотите, чтобы они перекрывались или определяли, когда они это делают, сделайте два элемента сбрасываемыми и заставьте их "принять" друг с другом:
$([div1, div2]).droppable(CONFIG_COLLISSION_PREVENTION_DROPPABLE);
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);
}
}
Обработчики "activ" и "drop" относятся к событиям "dropactivate" и "drop" плагина "droppable"
Здесь ключом является обратный вызов "drop". Всякий раз, когда какой-либо из двух элементов перекрывается, и они сбрасываются друг на друга, вызывается "отбрасывание". Это место для обнаружения и выполнения действий, может быть отправка пользовательских событий или вызов других действий (здесь я решил вернуть позиции перекрывающегося элемента в исходную позицию, когда началось перетаскивание, которое было зафиксировано в обратном вызове 'activate').
Вот и все. Никаких опросов, никаких плагинов, только встроенные события.
Ну, могут быть сделаны другие оптимизации / расширения, это был просто первый выстрел из моей головы, который работал:)
Вы также можете использовать события "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);
}
});
Надеюсь это поможет!
РЕДАКТИРОВАТЬ: я написал сообщение в блоге на моем сайте. Вот ссылка на него. http://area36.nl/2014/12/creating-your-own-collision-detection-function-in-javascript/
Ну, у меня была та же проблема, но благодаря ответу Оскара Годсона я получил функцию, которая работает. Я использовал Jquery для легкого кодирования и потому что я ленив; Я поместил функцию в другую функцию, которая запускается каждую секунду, так что имейте это в виду.
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
}
}
Что он делает, это в основном он получает все значения element1
а затем получить все значения element2
, Затем с помощью некоторых вычислений вычисляются все значения. Тогда в if
Заявление сравнивает квадрат element1
на площадь element2
, Если значения element1
находятся между левым, правым, верхним и нижним значениями element2
, Если это правда, код внизу выполняется.
Я сам столкнулся с этой обобщенной проблемой, поэтому (полное раскрытие) я сделал для нее плагин. Для простых коллизионных запросов о статических объектах попробуйте это:
http://sourceforge.net/projects/jquerycollision/
Который позволяет вам получить список перекрывающихся блоков столкновений (или ни одного, если нет столкновений):
hits = $("#collider").collision(".obstacles");
Или, чтобы получить событие столкновения во время "перетаскивания", используйте это:
http://sourceforge.net/apps/mediawiki/jquidragcollide/?source=navbar
Что дает вам событие "столкновения" для подключения. (Или событие "выпячивание", чтобы увидеть, не выходит ли div из другого div, который в настоящее время содержит его.)
$(draggable).bind(
"collision",
function(event,ui) {
...
}
);
Если вы проверяете столкновения во время движения, кроме перетаскивания, просто вызывайте оригинал несколько раз, это довольно быстро. Примечание: перетаскивание не очень хорошо с изменением размера.
Сообщение старое, может быть, оно кому-нибудь поможет...
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");
}
}