本文介绍了为jQuery UI Droppable的Intersect公差构建匹配选项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将一个元素拖到两个或更多可放置区域中,但是这些可放置区域必须完全包含在我的可拖动对象中.

I want to drag an element into TWO OR MORE droppable areas, but those droppable areas need to be wholly contained within my draggable object.

问题是, jQuery UI的现有可投放公差的功能都无法满足这种需求.

理想情况下,我会使用相交",但是可拖动对象和可拖放对象的度量在代码中是相反的. (可以通过搜索$.ui.intersectjquery-ui.js中找到此逻辑.)

Ideally I'd use "intersect", but where the the draggable and droppable object measurements are reversed in the code. (This logic can be found in jquery-ui.js by searching for $.ui.intersect.)

我尝试通过使用jQuery进行鸭打孔,并尝试将tolerance设置为这样的自定义函数:

I have tried overriding that function by duck punching with jQuery and tried setting the tolerance to a custom function like this:

tolerance: function(draggable, droppable) {
            if(!droppable.offset) return false;

            return ...logic check here...
        },
        drop: ...continues...

都没有.

这里是一个JSFiddle来说明我的意思: https://jsfiddle.net/myingling/kgaqb0ay/5/

Here is a JSFiddle to illustrate what I mean: https://jsfiddle.net/myingling/kgaqb0ay/5/

同样,//由一个人遮盖的所有物品都应分配.

Again, all Items //covered// by a person should be assigned.

推荐答案

修改 $.ui.intersect 似乎是最好的方法.您有不同的选择.如果您不需要那么多的灵活性,则可以简单地添加 tolerance 类型,例如"cover".然后,您只需要在开关中添加一个案例,以检查相交中的公差类型,这可以恰好是"fit"的倒数.像这样:

Modifying $.ui.intersect seems to be the best approach. You have different options. If you don't need that much flexibility, you can simply add a tolerance type, 'cover' for example. Then you just need to add a case to the switch that checks the tolerance type in intersect, which can be precisely the inverse of 'fit'. Like this:

  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/

或者,如果您想获得更大的灵活性,则可以添加 tolerance (公差)功能.然后,您可以在该选项中传递一个函数,该函数可让您对不同的可放置对象具有精确的公差.例如:在 interserct 功能中:

Or if you want more flexibility, you add a case where tolerance is a function. Then you can pass a function in the option, which allows you to have precise tolerance for different droppable. Something like this for example:In interserct function:

 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 ,也不会使用它.对其进行自定义要复杂一些.您可以通过这种方式进行操作,基本上是先对 intersect 进行作用域划分,然后在 $.ui.ddmanager ,以便调用修改后的相交:

From jquery 1.12 $.ui.intersect function is scoped so that it cannot be directly modified afterwards. It is called in $.ui.ddmanager as a local variable, so even if you modify $.ui.intersect, it won't be used. Customizing it is a bit more complex. You can do it this way, basically you rescope intersect and then redefine drag and drop method on $.ui.ddmanager so that it calls the modified 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;
            }
        }
    };
} )();

然后,您无需进行任何更改,就可以完全相同地重新定义它们.

Then this, where you don't change anything, you just redefine them exactly the same way.

$.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/

很显然,这一点更容易出错,并且可能会有更好的方法来实现这一目标.通常,您可以扩展小部件,例如,这样会更清洁.但是 intersect ddmanager 都在可拖动可拖放中使用,而不能直接在这些小部件中使用.因此,很难以一种干净的方式进行扩展.您也可以将逻辑直接放在可拖动对象和可放置对象的拖动事件和放置事件中,但是由于存在默认公差,因此不确定它会更好.

Obviously, this one is a bit more prone to errors and there might be a better way to achieve this. Normally you could extend the widgets for example, which would be cleaner. But intersect and ddmanager are used both in draggable and droppable and are not directly in these widgets. So it's harder to extend in a clean way.You could also put the logic directly in drag event and drop event of you draggables and droppables, but since there's a default tolerance, not sure it's much better.

这篇关于为jQuery UI Droppable的Intersect公差构建匹配选项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-30 17:50