简体   繁体   中英

jQueryUI drop on target closest to mouse

I'm using jQueryUI's draggable and droppable interactions, and in my application I only want to allow a draggable to be dropped into a single drop target, ideally the target that is closest to the mouse, or the target that has the most overlap with the draggable. The problem is that my drop targets can sometimes be close together, and it is very possible that a draggable will overlap more than one drop target. When my draggable overlaps more than one drop target, I'd like to set the hoverClass on only one of the drop targets, and only allow the drop on that target. Is this possible with jQueryUI?

I like the behavior of the 'touch' tolerance, so I'd like to keep that if possible. Here is a jsfiddle that demonstrates the problem I'm trying to solve: http://jsfiddle.net/gDkAQ/2/

$('.draggable').draggable();

$('.droppable').droppable({
    tolerance: 'touch',
    hoverClass: 'drop-acceptable',
    drop: function(event, ui) {
        $(this).css({ 'background-color': '#00f' });
    }
});

There is another library from ThreeDubMedia that provides the exact feature I'm looking for (the "overlap" tolerance mode), but unfortunately that library only seems to work with absolute-positioned elements, so I don't think it would work in my application.

I came up with a solution, but it involves fragile duck punching of jQueryUI. There is an internal function called $.ui.intersect that determines if a draggable can be dropped onto a droppable. There is also a $.ui.ddmanager.droppables collection that contains a list of all droppables on the page. I overrode the intersect function to support a 'touch-closest-to-mouse' toleranceMode that finds the closest droppable to the mouse, and only returns true if the current droppable is the closest. Here is the solution I came up with:

(function () {
    var defaultIntersect = $.ui.intersect;
    var cursorX, cursorY;

    $(document).mousemove(function (e) {
        cursorX = e.pageX;
        cursorY = e.pageY;
    });

    $.ui.intersect = function (draggable, droppable, toleranceMode) {
        if (toleranceMode !== 'touch-closest-to-mouse') {
            return defaultIntersect(draggable, droppable, toleranceMode);
        }
        if (!defaultIntersect(draggable, droppable, 'touch')) {
            return false;
        }
        var acceptable = _.filter($.ui.ddmanager.droppables.default, function (d) {
            return defaultIntersect(draggable, d, 'touch');
        });
        var closest = _.min(acceptable, function (other) {
            var otherCenterX = other.offset.left + other.proportions().width / 2;
            var otherCenterY = other.offset.top + other.proportions().height / 2;
            return Math.sqrt(Math.pow(otherCenterX - cursorX, 2) + Math.pow(otherCenterY - cursorY, 2));
        });
        return droppable === closest;
    };

    $('.draggable').draggable();

    $('.droppable').droppable({
        tolerance: 'touch-closest-to-mouse',
        hoverClass: 'drop-acceptable',
        drop: function (event, ui) {
            $(event.target).css({
                'background-color': '#0f0'
            });
        }
    });
})();

This works with jQueryUI 1.10.4. Here is a jsfiddle with the working solution: http://jsfiddle.net/gDkAQ/12/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM