Jquery可拖动并可使用CSS'zoom'属性进行放置

时间:2014-01-16 16:01:50

标签: jquery css zoom draggable droppable

在我的应用程序中,我有一个可以改变CSS缩放的div。

不幸的是,这与坐标空间混淆。当超过可缩放区域1页px!= 1px时:)

当涉及到droppables时,它会完全打破交叉点检测。

我正在寻找有关我应该从哪里开始尝试处理此问题的提示?

我在这里做了一个演示。

小提琴:

http://jsfiddle.net/crRvp/1/

HTML:

<div id="dse">
    <div id="ds">Drop here!</div>
</div>
<p>
<div id="widget">Drag me!</div>
</p>

CSS:

#dse{
    position:relative;
    width:640px;
    height:360px;
    zoom:0.75;
    -moz-transform: scale(0.75);
    -moz-transform-origin:0 0;
    -o-transform: scale(0.75);
    -o-transform-origin:0 0;
}

#ds{
    position:relative;
    zoom:1;
    width:640px;
    height:360px;
    border: solid 1px black;
    font-size:2em;
}

#widget{
    position:relative;
    width:150px;
    height:50px;
    border: solid 1px black;
}

.droppableActive{
    background-color:red;
}

.droppableHover{
    background-color:green;
}

JS:

$("#widget").draggable({
    opacity     : 0.7,
    scroll      : false,
    revert: function(dropped){
          var wasDropped = dropped && dropped[0].id == "ds";
          if(!wasDropped){$(this).animate({ top: 0, left: 0 }, 'slow');}
          return !wasDropped;
    }
    ,
    stop: function(evt, ui) {

        var canvasLeft = $('#ds').offset().left;
        var canvasTop = $('#ds').offset().top;

        alert('ui.position.left: ' + (ui.position.left) + ' canvasLeft: ' + canvasLeft);
        alert('ui.position.top: ' + (ui.position.top) + ' canvasTop: ' + canvasTop);
    }
});

$("#ds").droppable({
    activeClass: 'droppableActive',
    hoverClass: 'droppableHover',
    accept: "#widget",
    drop: function(evt, ui){
        id = ui.draggable.attr('id');
        alert('id: ' + id);
    }
});

1 个答案:

答案 0 :(得分:0)

我遇到了同样的问题,但仅在我的情况下,这两个问题:可拖放项和可拖动项都在缩放容器中(具有zoom css属性!)。

可拖动可以轻松地通过“ dragFix”修复程序进行修复:

var zoomScale = parseFloat($('.container').css('zoom'));

$('.drag__item').draggable({            
        drag: function (event, ui) {
            var changeLeft = ui.position.left - ui.originalPosition.left;
            var newLeft = ui.originalPosition.left + changeLeft / zoomScale;
            var changeTop = ui.position.top - ui.originalPosition.top;
            var newTop = ui.originalPosition.top + changeTop / zoomScale;

            ui.position.left = newLeft;
            ui.position.top = newTop;
        }
});

要修复可投放,我们需要编辑jquery-ui代码。考虑到编辑库源代码不是一个好主意,让我们重写intersect方法。 问题是x1y1坐标(可拖动项目的顶部,左)在先前的“ dragFix”修复后被破坏,因此,交集检测无法按预期进行。 因此,我们需要归一化x1y1坐标。

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.offset.left + draggable.position.left - draggable.originalPosition.left + draggable.margins.left,//here is the fix for scaled container
            y1 = draggable.offset.top + draggable.position.top - draggable.originalPosition.top + draggable.margins.top,//here is the fix for scaled container
            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;

        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;
        }
    };
})();
  

从jquery 1.12 $ .ui.intersect函数开始,它的作用域是一定范围的,因此以后不能直接对其进行修改。它在$ .ui.ddmanager中被称为局部变量,因此即使您修改$ .ui.intersect,也不会使用它。对其进行自定义要复杂一些。您可以通过这种方式进行操作,基本上是先检查相交,然后在$ .ui.ddmanager上重新定义拖放方法,以便它调用修改后的相交:

from this answer

$.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;
}
  

JSFiddle example