带有碰撞检测的 jQuery 拖动
Posted
技术标签:
【中文标题】带有碰撞检测的 jQuery 拖动【英文标题】:jQuery Dragging With Collision Detection 【发布时间】: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 做的是:
-
能够将
.item
s 从#list
拖入#timeline
.item
s 可以根据需要多次放入时间线,例如。时间线中可能有 4 个项目 #i1
。
时间线上的.item
s 不能相互重叠
.item
s 可以放置在时间轴上的任何位置,只要它们不与时间轴上的任何其他项目重叠
所以我选择了 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(项目不重叠)。
有人知道这个问题的解决方案吗?是否有另一个插件可以对可拖动对象的助手进行碰撞检测?有没有其他方法可以尝试获得我想要实现的目标?
【问题讨论】:
您可能需要使用可拖动作为实时事件 [***.com/questions/1805210/… [1]: ***.com/questions/1805210/… @dwiekropki 这将如何处理碰撞检测? 您可能需要等待修复此错误:sourceforge.net/p/jquidragcollide/bugs/3 【参考方案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>
享受:)。
【讨论】:
以上是关于带有碰撞检测的 jQuery 拖动的主要内容,如果未能解决你的问题,请参考以下文章