我想将一个元素拖动到两个或更多可放置的区域,但这些可放置的区域需要完全包含在我的可拖动对象中。
问题是,jQuery UI's existing functionality for droppable tolerances都没有满足这种需求。
理想情况下,我使用" intersect",但是在代码中可拖动和可放置的对象测量值被反转。 (通过搜索jquery-ui.js
可以在$.ui.intersect
中找到此逻辑。)
我尝试通过duck punching with jQuery覆盖该功能,并尝试将tolerance
设置为这样的自定义函数:
tolerance: function(draggable, droppable) {
if(!droppable.offset) return false;
return ...logic check here...
},
drop: ...continues...
两者都没有。
这是一个JSFiddle来说明我的意思:https://jsfiddle.net/myingling/kgaqb0ay/5/
同样,应该分配一个人//所涵盖的所有项目。
答案 0 :(得分:2)
修改 $ .ui.intersect 似乎是最好的方法。你有不同的选择。如果您不需要那么大的灵活性,您可以简单地添加容差类型,例如'cover'。然后你只需要在开关中添加一个用于检查相交中的公差类型的情况,这可能恰好与“适合”相反。像这样:
case 'fit':
return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
break;
case 'cover':
return (l >= x1 && x2 >= r && t >= y1 && y2 >= b);
break;
请参阅:https://jsfiddle.net/6nyqja4a/4/
或者如果您想要更多灵活性,可以添加容差是一个功能的案例。然后,您可以在选项中传递一个函数,这样您就可以对不同的可放置对象进行精确容差。像这样的东西例如: 在 interserct 函数中:
if (toleranceMode instanceof Function) {
return toleranceMode(draggable, droppable, x1, x2, y1, y2, l, r, t, b);
} else {
switch (toleranceMode) {
case 'fit':
return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
break;
...
你这样称呼它:
$('.droppable').droppable({
hoverClass: "yellow",
tolerance: function(drag, drop, x1, x2, y1, y2, l, r, t, b) {
return (l >= x1 && x2 >= r && t >= y1 && y2 >= b);
},
drop: function(event, ui) {
$("#value_" + $(this).data("id")).val(ui.draggable.data("id"));
console.log("Item " + $(this).data("id") + " taken by " + ui.draggable.data("id"));
}
});
请参阅:https://jsfiddle.net/h4wm3r09/3/
来自jquery 1.12 $ .ui.intersect 函数的作用域,以便之后无法直接修改。它在 $。ui.ddmanager 中作为局部变量调用,因此即使您修改 $ .ui.intersect ,也不会使用它。定制它有点复杂。您可以这样做,基本上您可以重新扫描交叉,然后在 $。ui.ddmanager上重新定义拖动和放置方法以便调用修改后的intersect:
var intersect = $.ui.intersect = ( function() {
function isOverAxis( x, reference, size ) {
return ( x >= reference ) && ( x < ( reference + size ) );
}
return function( draggable, droppable, toleranceMode, event ) {
if ( !droppable.offset ) {
return false;
}
var x1 = ( draggable.positionAbs ||
draggable.position.absolute ).left + draggable.margins.left,
y1 = ( draggable.positionAbs ||
draggable.position.absolute ).top + draggable.margins.top,
x2 = x1 + draggable.helperProportions.width,
y2 = y1 + draggable.helperProportions.height,
l = droppable.offset.left,
t = droppable.offset.top,
r = l + droppable.proportions().width,
b = t + droppable.proportions().height;
if (toleranceMode instanceof Function) {
return toleranceMode(draggable, droppable, x1, x2, y1, y2, l, r, t, b);
} else {
switch ( toleranceMode ) {
case "fit":
return ( l <= x1 && x2 <= r && t <= y1 && y2 <= b );
case "intersect":
return ( l < x1 + ( draggable.helperProportions.width / 2 ) && // Right Half
x2 - ( draggable.helperProportions.width / 2 ) < r && // Left Half
t < y1 + ( draggable.helperProportions.height / 2 ) && // Bottom Half
y2 - ( draggable.helperProportions.height / 2 ) < b ); // Top Half
case "pointer":
return isOverAxis( event.pageY, t, droppable.proportions().height ) &&
isOverAxis( event.pageX, l, droppable.proportions().width );
case "touch":
return (
( y1 >= t && y1 <= b ) || // Top edge touching
( y2 >= t && y2 <= b ) || // Bottom edge touching
( y1 < t && y2 > b ) // Surrounded vertically
) && (
( x1 >= l && x1 <= r ) || // Left edge touching
( x2 >= l && x2 <= r ) || // Right edge touching
( x1 < l && x2 > r ) // Surrounded horizontally
);
default:
return false;
}
}
};
} )();
然后,在你不改变任何东西的地方,你只需要完全相同的方式重新定义它们。
$.ui.ddmanager.drag = function( draggable, event ) {
// If you have a highly dynamic page, you might try this option. It renders positions
// every time you move the mouse.
if ( draggable.options.refreshPositions ) {
$.ui.ddmanager.prepareOffsets( draggable, event );
}
// Run through all droppables and check their positions based on specific tolerance options
$.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() {
if ( this.options.disabled || this.greedyChild || !this.visible ) {
return;
}
var parentInstance, scope, parent,
intersects = intersect( draggable, this, this.options.tolerance, event ),
c = !intersects && this.isover ?
"isout" :
( intersects && !this.isover ? "isover" : null );
if ( !c ) {
return;
}
if ( this.options.greedy ) {
// find droppable parents with same scope
scope = this.options.scope;
parent = this.element.parents( ":data(ui-droppable)" ).filter( function() {
return $( this ).droppable( "instance" ).options.scope === scope;
} );
if ( parent.length ) {
parentInstance = $( parent[ 0 ] ).droppable( "instance" );
parentInstance.greedyChild = ( c === "isover" );
}
}
// We just moved into a greedy child
if ( parentInstance && c === "isover" ) {
parentInstance.isover = false;
parentInstance.isout = true;
parentInstance._out.call( parentInstance, event );
}
this[ c ] = true;
this[ c === "isout" ? "isover" : "isout" ] = false;
this[ c === "isover" ? "_over" : "_out" ].call( this, event );
// We just moved out of a greedy child
if ( parentInstance && c === "isout" ) {
parentInstance.isout = false;
parentInstance.isover = true;
parentInstance._over.call( parentInstance, event );
}
} );
}
$.ui.ddmanager.drop = function( draggable, event ) {
var dropped = false;
// Create a copy of the droppables in case the list changes during the drop (#9116)
$.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), function() {
if ( !this.options ) {
return;
}
if ( !this.options.disabled && this.visible &&
intersect( draggable, this, this.options.tolerance, event ) ) {
dropped = this._drop.call( this, event ) || dropped;
}
if ( !this.options.disabled && this.visible && this.accept.call( this.element[ 0 ],
( draggable.currentItem || draggable.element ) ) ) {
this.isout = true;
this.isover = false;
this._deactivate.call( this, event );
}
} );
return dropped;
}
https://jsfiddle.net/u6wfj8mj/1/
显然,这个更容易出错,可能有更好的方法来实现这一点。通常你可以扩展小部件,例如,它会更干净。但交叉和 ddmanager 在可拖动和可放置中均使用,并且不直接在这些小部件中使用。因此,以一种干净的方式扩展起来更加困难。 您也可以将逻辑直接放在拖动事件中并放下拖动事件和droppables事件,但由于存在默认容差,因此不确定它是否更好。
答案 1 :(得分:0)
当左上角进入内部时,如何使jQuery UI的Droppable接受Draggable? 我为jQuery UI Droppable 1.9.2创建了jquery.ui.droppable-patch.js
(function ($, undefined) {
$.ui.intersect = function (draggable, droppable, toleranceMode) {
if (!droppable.offset) return false;
var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
var l = droppable.offset.left, r = l + droppable.proportions.width,
t = droppable.offset.top, b = t + droppable.proportions.height;
switch (toleranceMode) {
case 'fit':
return (l <= x1 && x2 <= r
&& t <= y1 && y2 <= b);
break;
case 'intersect':
return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
&& x2 - (draggable.helperProportions.width / 2) < r // Left Half
&& t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
&& y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
break;
case 'pointer':
var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
return isOver;
break;
case 'touch':
return (
(y1 >= t && y1 <= b) || // Top edge touching
(y2 >= t && y2 <= b) || // Bottom edge touching
(y1 < t && y2 > b) // Surrounded vertically
) && (
(x1 >= l && x1 <= r) || // Left edge touching
(x2 >= l && x2 <= r) || // Right edge touching
(x1 < l && x2 > r) // Surrounded horizontally
);
break;
case "top-left-touch":
return ( y1 >= t && y1 <= b ) && ( x1 >= l && x1 <= r );
case "top-right-touch":
return ( y1 >= t && y1 <= b ) && ( x2 >= l && x2 <= r );
case "bottom-left-touch":
return ( y2 >= t && y2 <= b ) && ( x1 >= l && x1 <= r );
case "bottom-right-touch":
return ( y2 >= t && y2 <= b ) && ( x2 >= l && x2 <= r );
default:
return false;
break;
}
};
}(jQuery));