【问题标题】:jQuery Dragging With Collision Detection带有碰撞检测的 jQuery 拖动
【发布时间】:2013-01-15 16:14:54
【问题描述】:

我有以下 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. 能够将.items 从#list 拖入#timeline
  2. .items 可以根据需要多次放入时间线,例如。时间线中可能有 4 个项目 #i1
  3. 时间线上的.items 不能相互重叠
  4. .items 可以放置在时间轴上的任何位置,只要它们不与时间轴上的任何其他项目重叠

所以我选择了 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 Draggable Collision Plugin 仅在您拖动原始 Div 本身时有效,而不是拖动助手。我需要帮手,这样我才能实现#2(添加一个项目的多个副本)。但我需要像 Collision Plugin 这样的东西,这样我才能实现 #3(项目不重叠)。

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

【问题讨论】:

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


【解决方案1】:

如果您更喜欢使用 jQueryUI Draggable Collision Plugin 的 jsfiddle,如您所建议的,这里有一些可以玩的东西:Link to jsfiddle

该方法使用原始助手来利用碰撞功能。 克隆在 start 事件函数中生成(并在 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();
});

希望这会有用。

【讨论】:

    【解决方案2】:

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

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

    /*----------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>

    享受:)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-24
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多