web-dev-qa-db-fra.com

Détection de collision jQuery / JavaScript

Comment détecter si deux <div> des éléments sont entrés en collision?

Les deux divs sont de simples boîtes colorées se déplaçant perpendiculairement l'une à l'autre, donc pas de formes ou d'angles compliqués.

75
Chris Armstrong
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>

Idée générale - vous obtenez le décalage et la dimension des cases et vérifiez si elles se chevauchent.

Si vous souhaitez le mettre à jour, vous pouvez utiliser setInterval:

function detectOverlapping() {
    // code that detects if the box overlaps with a moving box
    setInterval(detectOverlapping, 25);
}

detectOverlapping();  

Notez également que vous pouvez optimiser la fonction pour votre exemple spécifique.

  • vous n'avez pas à lire les dimensions de la boîte à plusieurs reprises (comme je le fais dans mon code) car elles sont fixes. Vous pouvez les lire au chargement de la page (dans une variable), puis simplement lire la variable

  • la position horizontale de la petite boîte ne change pas (sauf si l'utilisateur redimensionne la fenêtre). Les positions verticales des caisses de voiture ne changent pas. Par conséquent, ces valeurs ne doivent pas non plus être lues de manière répétée, mais peuvent également être stockées dans des variables.

  • vous n'avez pas à tester à tout moment si la petite boîte chevauche toutes les boîtes de voiture. Vous pouvez - en fonction de sa position verticale - déterminer dans quelle voie la boîte se trouve actuellement et tester uniquement la boîte de voiture spécifique de cette voie.

70
Šime Vidas

Je pense que c'est le moyen le plus simple: http://plugins.jquery.com/project/collidable

En voici une autre, en allemand: http://www.48design.de/news/2009/11/20/kollisionsabfrage-per-jquery-plugin-update-v11-8/

Je les essayerais.

--MISE À JOUR--

Je ne peux pas vraiment y consacrer de temps en ce moment, mais je peux quand je rentre à la maison si personne ne répond mais vous; faites quelque chose comme:

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);
17
Oscar Godson

C'est un peu tard, mais je suppose que vous pourriez utiliser cette approche que j'ai essayée lorsque j'étais confronté à une situation similaire. L'avantage ici est qu'il n'y a pas de plugin supplémentaire, ni de scripts impliqués et vous n'avez pas non plus à y introduire des sondages gourmands en performances. Cette technique utilise les méthodes et événements intégrés que Jquery's droppable a à offrir.

Ok, assez dit, voici la technique de la solution: dites si vous avez deux éléments (des images dans mon cas) et que vous ne voulez pas qu'ils se chevauchent ou détectent quand ils le font, faites en sorte que les deux éléments soient largables et faites-les accepter L'un et l'autre:

$([div1, div2]).droppable(CONFIG_COLLISSION_PREVENTION_DROPPABLE);

Le 'CONFIG_COLLISSION_PREVENTION_DROPPABLE' ressemble à ceci:

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

Les gestionnaires 'activate' et 'drop' font référence aux événements 'dropactivate' et 'drop' du plugin "droppable"

Ici, la clé est le rappel 'drop'. Chaque fois que l'un des deux éléments se chevauchent et qu'ils sont déposés l'un sur l'autre, le "drop" sera appelé. C'est l'endroit pour détecter et entreprendre des actions, peut envoyer des événements personnalisés ou appeler d'autres actions (j'ai choisi ici de ramener les positions de l'élément qui se chevauchent à la position initiale lorsque le glissement a commencé, qui a été capturé dans le rappel `` activer '').

C'est ça. Pas d'interrogation, pas de plugins, juste les événements intégrés.

Eh bien, il peut y avoir d'autres optimisations/extensions, c'était tout simplement le premier coup de tête qui a fonctionné :)

Vous pouvez également utiliser les événements "dropover" et "dropout" pour signaler et créer un retour visuel à l'utilisateur que deux éléments se chevauchent, alors qu'ils peuvent toujours être en mouvement.

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

J'espère que cela t'aides!

7
Murtaza H

EDIT: J'ai écrit un article de blog sur mon site Web. Voici un lien vers celui-ci. http://area36.nl/2014/12/creating-your-own-collision-detection-function-in-javascript/

Eh bien, j'ai eu le même problème mais grâce à la réponse d'Oscar Godson, j'ai obtenu une fonction qui fonctionne. J'ai utilisé Jquery pour un codage facile et parce que je suis paresseux; p. J'ai mis la fonction dans une autre fonction qui est déclenchée à chaque seconde, alors gardez cela à l'esprit.

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
    }
}

Ce qu'il fait, c'est qu'il obtient toutes les valeurs de element1 puis récupérez toutes les valeurs de element2. Ensuite, à l'aide de quelques calculs, il calcule toutes les valeurs. Ensuite, dans l'instruction if, il compare le carré de element1 au carré de element2. Si les valeurs de element1 sont entre les valeurs gauche, droite, haut et bas de element2. Si c'est vrai, le code en bas est exécuté.

5
Geke_Sulen

J'ai moi-même rencontré ce problème généralisé, alors (divulgation complète), j'ai créé un plugin pour cela. Pour les requêtes de collision simples sur les objets statiques, essayez ceci:

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

Ce qui vous permet d'obtenir une liste de boîtes de collision qui se chevauchent (ou aucune s'il n'y a pas de collision):

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

Ou pour obtenir un événement de collision pendant le "glisser", utilisez ceci:

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

Ce qui vous donne un événement "collision" auquel vous connecter. (Ou un événement de "protrusion", pour voir si un div échappe à un autre div qui le contient actuellement.)

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

Si vous vérifiez les collisions pendant un mouvement autre que le glissement, appelez simplement l'original à plusieurs reprises, c'est assez rapide. Remarque: celui qui fait glisser ne fonctionne pas bien avec le redimensionnement.

3
eruciform

Le message est vieux, peut-être aider quelqu'un ...

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
Anoop B.K