jQuery拖动与碰撞检测

时间:2013-01-31 14:15:50

标签: javascript jquery collision-detection jquery-ui-draggable

我有以下HTML:

<div class="list" id="list">
    <div class="item" id="i1">Item 1</div>
    <div class="item" id="i2">Item 2</div>
    <div class="item" id="i3">Item 3</div>
</div>
<div class="timeline" id="timeline">
</div>

我想用jQuery做的是:

  1. 能够将.item#list拖到#timeline
  2. .item可以根据需要多次放入时间线,例如。时间线中可能有4个#i1项。
  3. 时间轴中的
  4. .item s不得相互重叠
  5. .item可以定位在时间轴上的任何位置,只要它们不与时间轴上的任何其他项重叠
  6. 所以我去了jQueryUI的Draggable和Droppable,也去了jQueryUI Draggable Collision Plugin

    这是我开始使用的jQuery:

    $('#list .item').draggable({
        helper: 'clone',
        revert: 'invalid',
        //the following are for the jquery-ui-dragggable-collision plugin
        obstacle: '#timeline .item',
        preventCollision: true
    });
    $('#timeline').droppable({
        accept: '.item'
    });
    

    我的问题是jQueryUI可拖动碰撞插件仅在拖动原始Div本身而不拖动帮助程序时才有效。我需要助手才能达到#2(添加一个项目的多个副本)。但我需要像碰撞插件这样的东西,所以我可以达到#3(物品不重叠)。

    有人知道这个问题的解决方案吗?是否有另一个插件可以对可拖动对象的助手进行碰撞检测?有没有其他方法可以尝试获得我想要实现的目标?

2 个答案:

答案 0 :(得分:3)

如果您更喜欢使用jsfiddle,如您所建议的那样使用jQueryUI Draggable Collision插件,可以使用以下内容:Link to jsfiddle

该方法使用原始帮助程序以利用碰撞功能。 克隆是在启动事件函数中生成的(如果拖动没有导致成功删除,则会在stop事件中再次删除):

$(function(){
    var draggableSelector = ".list .item:not(.dropped)";
    var init = function() {  
        $(draggableSelector).each(function(i){
            $(this)
                .draggable({
                    //helper: 'clone',
                    revert: 'invalid',
                    start: function(event,ui) {
                        var $clone = ui.helper.clone();
                        $clone
                            .removeClass("ui-draggable ui-draggable-dragging")
                            .insertAfter(ui.helper)
                        ;
                        $(this).data("clone",$clone);
                    },
                    stop: function(event,ui) {
                        if( $(".ui-draggable-dragging.dropped").length == 0) {
                            $(this).data("clone").remove();
                        };
                    },
                    //the following are for the jquery-ui-draggable-collision plugin
                    refreshPositions: true,
                    obstacle: '.item.dropped',
                    preventCollision: true,
                })
                .css("left", ( ($(this).width() + 5) * i) + "px")
            ;
        });

        $('.timeline').droppable({
            accept: '.item'
            ,drop: function(event,ui) {
                ui.draggable
                    .addClass("dropped")
                ;
                setTimeout(reinit, 500);
            }
        });                
    };

    var reinit = function() {
        $(".list .item.ui-draggable").draggable("destroy");
        init();
    }

    init();
});

希望这会有用。

答案 1 :(得分:1)

这是我为这个问题写的一个例子,显示了一个带有碰撞检测的简单拖放插件。 只要项目存在空间而不重叠,它允许您将项目拖放到时间轴上。

它绝不是一个完成的产品,但希望能够证明这样的代码编写起来并不是非常复杂,并且试图破解大量冲突的插件并不总是最好的选择。有时最好从头开始。它很有趣,也是一种非常好的学习方式。

/*----------ON DOCUMENT READY----------*/

$(document).ready(function() {

  $("#timeline").timeline({
    items: ".item"
  });

});

/*----------THE TIMELINE PLUGIN----------*/

$.fn.timeline = function(options) {

  var defaults = {
    items: "div"
  }

  var options = $.extend(defaults, options)

  return this.each(function() {

    //-----SETUP-----//

    //define all the vars we will need later
    var el = $(this);
    var items = $(options.items);
    var mousedown = false;
    var dragging = false;
    var activeItem = false;
    var placedItems = new Array();

    //make everything unselectable so it dosne interfere with dragging
    $("html").find("*").css({
      "user-select": "none",
      "-moz-user-select": "none",
      "-webkit-user-select": "none",
      "-ms-user-select": "none",
      "-o-user-select": "none",
    }).attr("unselectable", "true").unbind("onselectstart");

    //-----EVENTS-----//

    //log when the mouse is down anywhere on the doc
    $(document).mousedown(function() {
      mousedown = true;
    });

    //when the mouse is released
    $(document).mouseup(function(e) {
      //if was dragging an item attempt to place it
      if (mousedown && dragging) {
        placeItem(e);
      }
      //log that dragging has stopped
      mousedown = false;
      dragging = false;
    });

    //log when the mouse is pressed over an item
    items.mousedown(function() {
      dragging = true;
      //clone the active item and hide it ready for dragging
      activeItem = $(this).clone().appendTo("body").hide();
    });

    //when the mouse movers over the doc
    $(document).mousemove(function(e) {
      //if mouse was pressed over item attempt to drag
      if (mousedown && dragging) {
        dragItem(e);
      }
    });

    //-----FUNCTIONS-----//

    //drag the item around the screen
    function dragItem(e) {

      //if no active item done do owt
      if (!activeItem) {
        return false;
      }

      //work out where the drag anchor is
      var x = e.pageX - (activeItem.height() / 2);
      var y = e.pageY - (activeItem.width() / 2);

      //save the original position in case we cant place the item
      if (!activeItem.origPos) {
        activeItem.origPos = {
          x: x,
          y: y
        }
      }

      //drag the item
      activeItem.css({
        "position": "absolute",
        "top": y,
        "left": x,
        "z-index": "999",
        "opacity": 0.6,
        "display": "block"
      });

    }

    //attempt to place the item
    function placeItem(e) {

      //if no active item dont do owt
      if (!activeItem) {
        return false;
      }

      //define som vars needed later on
      var onTargetY = false;
      var onTargetX = false;
      var remove = false;
      var collision = false;

      //check if item is being relesed withing the timeline bounds
      if (e.pageY > el.position().top && e.pageY < el.position().top + el.height()) {
        onTargetY = true;
      }
      if (e.pageX > el.position().left && e.pageX < el.position().left + el.width()) {
        onTargetX = true;
      }

      //if on target attempt to drop on timeline
      if (onTargetX && onTargetY) {

        //snap to the left or right if dropped at the left or right edges
        var maxLeft = el.position().left;
        var maxRight = el.position().left + el.width() - activeItem.width();
        x = e.pageX - (activeItem.width() / 2);
        if (x < maxLeft) {
          x = maxLeft;
        } else if (x > maxRight) {
          x = maxRight;
        }

        //loop the items already on the timeline and check for collisions
        $.each(placedItems, function(i, item) {
          var itemMin = item.position().left;
          var itemMax = item.position().left + item.width();
          if (x + activeItem.width() > itemMin && x < itemMax) {
            collision = true;
          }
        });
        y = el.position().top;

      }

      //if there is a collision or the item is dropped outside the timeline 
      //set x and y back to original position and set removal flag to true
      if (collision || !onTargetX || !onTargetY) {
        x = activeItem.origPos.x;
        y = activeItem.origPos.y;
        remove = true;
        //if dropped inside the timeline and no collisions add item to the 
        //array of items inside the timeline
      } else {
        placedItems.push(activeItem);
      }

      //finally either animate the item back to where it started then remove it
      //or snap it into the timeline in the space found
      activeItem.animate({
        top: y,
        left: x
      }, {
        duration: 300,
        queue: false,
        complete: function() {

          //if remove flag set remove the item from the dom
          if (remove) {
            $(this).remove();
          }

          //some tidying up
          activeItem.css("opacity", 1);
          activeItem = false;

        }
      });

    }

  });

}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="list" id="list">
  <div class="item item1">Item 1</div>
  <div class="item item2">Item 2</div>
  <div class="item item3">Item 3</div>
</div>

<div class="timeline" id="timeline"></div>

享受:)。