代码之家  ›  专栏  ›  技术社区  ›  Chris Armstrong

jQuery/JavaScript冲突检测

  •  72
  • Chris Armstrong  · 技术社区  · 14 年前

    如何检测两个 <div> 元素碰撞了?

    这两个div是相互垂直的简单彩色盒子,因此没有复杂的形状或角度。

    6 回复  |  直到 9 年前
        1
  •  70
  •   Gajus    9 年前

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

    另外,请注意,您可以针对特定示例优化函数。

    • 您不必重复阅读框的尺寸(就像我在代码中所做的那样),因为它们是固定的。您可以在页面加载时读取它们(到变量中),然后只读取变量

    • 小框的水平位置不会改变(除非用户调整窗口大小)。轿厢的垂直位置不变。因此,这些值也不必重复读取,但也可以存储到变量中。

    • 你不必在任何时候测试这个小盒子是否与所有的汽车盒子重叠。你可以-基于它的垂直位置-找出盒子当前在哪个车道上,并且只测试那个车道上的特定汽车盒子。

        2
  •  17
  •   Oscar Godson    14 年前

    我相信这是最简单的方法: 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);
    
        3
  •  7
  •   Murtaza H    12 年前

    这有点晚了,但我想你可以使用我在面对类似情况时尝试的方法。这里的优点是不需要额外的插件或脚本,也不必在其中引入性能要求很高的轮询。 此技术使用Jquery的droppable必须提供的内置方法和事件。

    好吧,说得够多了,下面是解决方法: 假设您有两个元素(在我的例子中是图像),并且您不希望它们重叠或在重叠时检测到它们,请将这两个元素设置为可拖放的,并使它们相互“接受”:

    $([div1, div2]).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”处理程序指的是“droppable”插件的“dropactivate”和“drop”事件

    这里,关键是“drop”回调。当两个元素中的任何一个重叠并且彼此重叠时,将调用“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);
        }
    });
    

    希望这有帮助!

        4
  •  5
  •   Dennis Heiden    8 年前

    编辑:我在我的网站上写了一篇博文。这里有一个链接。 http://area36.nl/2014/12/creating-your-own-collision-detection-function-in-javascript/

    我也有同样的问题,但多亏了奥斯卡·戈德森的回答,我得到了一个有效的函数。我使用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
        }
    }
    

    它所做的基本上是得到 element1 然后得到 element2 . 然后在一些计算的帮助下,它计算出所有的值。然后在 if 它比较的是 要素1 到广场 要素2 . 如果 要素1 位于的左、右、顶部和底部值之间 要素2 . 如果是这样,则执行底部的代码。

        5
  •  3
  •   eruciform    12 年前

    我自己也遇到了这个普遍的问题,所以(完全公开)我做了一个插件。对于有关静态对象的简单碰撞查询,请尝试以下操作:

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

    如果在运动过程中检查碰撞而不是拖动,只需反复调用原始对象,这非常快。注意:拖拽的不适合调整大小。

        6
  •  1
  •   Anoop B.K    9 年前

    邮政是旧的,也许它可以帮助某人。。。

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