jQuery / JavaScript обнаружение коллизий - PullRequest
73 голосов
/ 20 ноября 2010

Как определить, столкнулись ли два <div> элемента?

Эти два элемента представляют собой простые цветные прямоугольники, движущиеся перпендикулярно друг другу, поэтому никаких сложных форм или углов.

Ответы [ 6 ]

70 голосов
/ 20 ноября 2010

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();  

Также обратите внимание, что вы можете оптимизировать функцию для вашего конкретного примера.

  • вам не нужно многократно читать размеры блока (как я это делаю в своем коде), поскольку они исправлены. Вы можете прочитать их при загрузке страницы (в переменную), а затем просто прочитать переменную

  • горизонтальное положение маленькой коробки не изменяется (если пользователь не изменяет размеры окна). Вертикальные положения автомобильных коробок не меняются. Следовательно, эти значения также не должны считываться повторно, но также могут храниться в переменных.

  • вам не нужно проверять, перекрывает ли маленькая коробочка все автомобильные ящики. Вы можете - на основе его вертикального положения - выяснить, в какой полосе находится данный бокс, и протестировать только конкретный автомобильный бокс из этой полосы.

17 голосов
/ 20 ноября 2010

Я считаю, что это самый простой способ: 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);
7 голосов
/ 01 апреля 2012

Это немного поздно, но я думаю, вы могли бы использовать этот подход, который я попробовал, когда столкнулся с подобной ситуацией. Преимущество здесь в том, что в нем нет дополнительных плагинов или сценариев, и вам также не нужно вводить в него опрос с высокой производительностью. В этом методе используются встроенные методы и события, которые может предложить 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'). 1011 *

Вот и все. Никаких опросов, никаких плагинов, только встроенные события.

Ну, могут быть сделаны другие оптимизации / расширения, это был просто первый выстрел из моей головы, который сработал:)

Вы также можете использовать события «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);
    }
});

Надеюсь, это поможет!

5 голосов
/ 28 апреля 2014

РЕДАКТИРОВАТЬ: я написал сообщение в блоге на моем сайте.Вот ссылка на него.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.Если это правда, код внизу выполняется.

3 голосов
/ 03 января 2013

Я столкнулся с этой обобщенной проблемой сам, поэтому (полное раскрытие) я сделал плагин для нее. Для простых коллизионных запросов о статических объектах попробуйте это:

http://sourceforge.net/projects/jquerycollision/

Который позволяет вам получить список перекрывающихся блоков столкновений (или ни одного, если нет столкновений):

hits = $("#collider").collision(".obstacles");

Или, чтобы получить событие столкновения во время «перетаскивания», используйте это:

http://sourceforge.net/apps/mediawiki/jquidragcollide/?source=navbar#collision

Что дает вам событие "столкновения" для подключения. (Или событие «выпячивание», чтобы увидеть, не выходит ли div из другого div, который в настоящее время его содержит.)

$(draggable).bind( 
   "collision",
   function(event,ui) {
      ...
   }
);

Если вы проверяете столкновения во время движения, отличного от перетаскивания, просто вызывайте оригинал несколько раз, это довольно быстро. Примечание: перетаскивание не очень хорошо с изменением размера.

1 голос
/ 13 августа 2015

Сообщение старое, может быть, оно кому-нибудь поможет ...

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");
    }
}
...