Виявлення зіткнення jQuery / JavaScript


77

Як виявити, чи <div>зіткнулися два елементи?

Два div - це прості кольорові коробки, які рухаються перпендикулярно один одному, тому відсутні складні форми або кути.


Ого, яка приємна веб-сторінка. А анімація - це чистий CSS. :)
Šime Vidas

дякую, це поки що грубо, але ви зрозуміли ідею. Я зраджу, коли зроблю основи. Виявляється, CSS є фантастичним для дизайну рівнів ... класи - це дуже простий спосіб поведінки шарів. Попробую ваш зразок коду зараз, дякую
Кріс Армстронг

4
Попередження, сторінка аварійно завершує роботу FireFox 12. JavaScript зависає, і він ніколи не просить зупинити сценарій.
Potatoswatter

Як підказка для вашої гри, ви можете відключити сторінку вниз і смугу прокрутки.
Ендрю

Відповіді:


71

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

Також зауважте, що ви можете оптимізувати функцію для свого конкретного прикладу.

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

  • горизонтальне положення коробочки не змінюється (якщо користувач не змінює розмір вікна). Вертикальне положення автомобільних коробок не змінюється. Отже, ці значення також не потрібно читати повторно, але їх також можна зберігати у змінних.

  • вам не потрібно перевіряти, чи перетинається коробочка з усіма ящиками для автомобілів постійно. Ви можете - виходячи з її вертикального положення - з’ясувати, на якій смузі знаходиться коробка в даний момент, і протестувати лише конкретну автомобільну коробку з цієї смуги.


це виглядає чудово, намагаючись перевірити це на своєму зразку зараз ... як би я змусив його оновити кожен кадр?
Кріс Армстронг,

Хм ... Позиція гравця знаходиться від (649, 75) до (674, 100), а позиція машини від (649, 50) до (749, 100), тому вони перекриваються, але матч повертається як хибне, будь-яка ідея, чому це буде?
Кріс Армстронг,

@Chris Yea, я щойно зрозумів, що врахував лише 2 із загальних 9 сценаріїв перекриття. Але включити всі 9 сценаріїв має бути легко. Дайте мені секунду
Шіме Відас,

1
@Chris Concentrate на виявленні, який "автомобіль" перевірити, виходячи з вертикального положення "гравця". У вас є 15 автомобілів, тестування всіх 15 автомобілів щоразу відбувається в 15 разів повільніше, ніж тестування лише одного автомобіля, який, можливо, може перекрити плеєр.
Шіме Відас,

4
Це дуже приємна скрипка, але чи не вважаєте ви, що її краще використовувати, $.offset()а не $.position()на рядку 4? Таким чином він повинен працювати для перевірки зіткнення всіх елементів у DOM, а не лише для роботи з елементами брата.
Стівен Лу

17

Я вважаю, що це найпростіший спосіб: http://plugins.jquery.com/project/collidable

Ось ще одна, німецькою мовою: http://www.48design.de/news/2009/11/20/kollisionsabfrage-per-jquery-plugin-update-v11-8/

Я спробую цим.

- UPDATE--

Я не можу зараз витратити на це будь-коли, але я можу, повернувшись додому, якщо ніхто не відповідає, крім вас;

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

Дякую! Чи потрібно змушувати об’єкти, які можна перетягувати, щоб він працював?
Кріс Армстронг

Скажи, ти знаєш що, я думаю, це робиться, ОДНО ... можливо, ти міг би встановити його для перетягування за допомогою інтерфейсу jQuery (який, на мою думку, і використовує), але потім встановити return false для натискання подій на об'єкті, роблячи його неможливим для натискання тому неможливо розтягнути), але зіткнення могло б спрацювати.
Оскар Годсон,

1
Ви знаєте ... Ви могли б також (а може бути і простіше) ... оновити мою відповідь.
Оскар Годсон,

7

Це трохи запізнюється з цим, але, мабуть, ви можете використати такий підхід, який я спробував, коли зіткнувся з подібною ситуацією. Перевага тут полягає в тому, що тут немає додаткового плагіна або сценаріїв, і ви також не повинні вводити в нього голодні опитування. Ця техніка використовує вбудовані методи та події, які може запропонувати випадаючий 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);
     }
}

Обробники 'activate' і 'drop' посилаються на події 'dropactivate' і 'drop' плагіна "droppable"

Тут ключовим є зворотний дзвінок "падіння". Всякий раз, коли будь-який з двох елементів перекривається і вони перекидаються один над одним, буде викликано "падіння". Це місце для виявлення та здійснення дій, можливо, розсилання користувацьких подій або виклик інших дій (я тут вирішив повернути позиції елемента, що перекриваються, у початкову позицію, коли почалося перетягування, яке було зафіксовано при зворотному виклику `` активувати '').

Це воно. Ні опитування, ні плагіни, лише вбудовані події.

Ну, можуть бути зроблені й інші оптимізації / розширення, це був просто перший вистріл з голови, який спрацював :)

Ви також можете використовувати події `` 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);
    }
});

Сподіваюся, це допомагає!


ВАЖЛИВО: також зверніть увагу, що спочатку два div слід зробити "перетяжними".
Murtaza H

Я шукав щось подібне! Намагаючись уникнути ігрового циклу, я не хотів вводити опитування в рівняння (як ви згадуєте). Дякую!
Chris Dolphin

5

EDIT: Я написав допис у блозі на своєму веб-сайті. Ось посилання на нього. 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. Якщо це правда, виконується код внизу.


Не могли б ви навести приклад? Можливо, скрипка JS?
Енді,

3

Я сам зіткнувся з цією узагальненою проблемою, тому (повне розкриття) я зробив для нього плагін. Для простих запитів про зіткнення щодо статичних об’єктів спробуйте наступне:

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

Якщо ви перевіряєте зіткнення під час руху, крім перетягування, просто зателефонуйте оригіналу кілька разів, це досить швидко. Примітка: перетягування не дуже добре грає зі зміною розміру.


Привіт, у вас є приклад зіткнення, яке працює на перетягування? Мені не вдалося використати його, щоб зупинити один div, коли він торкається іншого, не перекриваючи їх. Під час випинання я роблю підйом, тому більше не можу перетягувати його назовні - мені потрібно зробити підведення миші перед випинанням.
alex toader

1

Публікація стара, нехай це комусь допоможе ...

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

1

Ви можете зробити це за допомогою 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)
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.