可拖动的代码无法与hammer.js一起使用

7
我已经成功实现了jQueryUI的可拖动功能,但是一旦我添加hammer.js代码,可拖动的代码就无法工作。
不是只要包含hammer.js,而是一旦使用该脚本就会出现问题。
为什么会这样?如何让它们都能工作?
可拖动和hammer都应用于.dataCard和#main。
在这里(未使用hammer.js实现)可拖动代码正常工作:http://goo.gl/MO5Pde 以下是可拖动代码示例:
$('#main').draggable({
    axis:'y',
    revert:true,
    start: function(event, ui){
        topValue = ui.position.top;
    },
    drag: function(event, ui){
            if(pastBreakpoint === false){
                $('#searchInput').blur();
                if(topValue > ui.position.top) return false;
            if(ui.position.top >= 161){
                if(pastBreakpoint === false){
                pastBreakpoint = true;
                    if($('.loadingRefresh').length === 0) $('#main').before('<div class="loadingRefresh"></div>');
                    else{
                    $('.loadingRefresh').remove();
                        $('#main').before('<div class="loadingRefresh"></div>');
                    }
                    $('.loadingRefresh').fadeIn();
                    $('#main').mouseup();
                    setTimeout(function(){
                       location.reload();
                    }, 1000);
                 }
              }
            }   
        }
    });

这里是未注释的锤子代码,可拖动的代码不起作用:http://goo.gl/994pxF 这里是锤子代码:
var hammertime = Hammer(document.getElementById('main'), {
    transform_always_block: true,
    transform_min_scale: 0
});

var posX = 0,
    posY = 0,
    lastPosX = 0,
    lastPosY = 0,
    bufferX = 0,
    bufferY = 0,
    scale = 1,
    last_scale = 1;


hammertime.on('touch transform transformend', function(ev) {

    if ((" " + ev.target.className + " ").indexOf(" dataCard ") < 0) return;

    else manageMultitouch(ev, ev.target); }); 

    function manageMultitouch(ev, element) { 

        switch (ev.type) { 
            case 'touch': 
                last_scale = scale;
                                return;
             case 'transform':
                scale = Math.min(last_scale * ev.gesture.scale, 10);
                break;
            }

            if(scale <= 0.5) $(element).hide('clip');

            if(scale > 1.0) $(element).addClass('focused');

            var transform = "translate(" + 0 + "px," + 0 + "px) " + "scale(" + 1 + "," + scale + ")";
            var style = element.style;
            style.transform = transform;
            style.oTransform = transform;
            style.msTransform = transform;
            style.mozTransform = transform;
            style.webkitTransform = transform;
     }  
3个回答

3
我在我的应用程序中遇到了相同的问题,即使包含touch punch。 我不得不进行深入研究才找到阻止jquery ui拖动的问题所在。 发生的问题是在事件中设置preventDefault(仅当包含hammer时),更改了jquery ui的触发方法的结果。
好的,让我们回到一点: 你应该看到的第一个方法是_mouseMove(),它与mousemove事件相关联。 只有当条件(this._mouseStart(this._mouseDownEvent, event) !== false)为真时,拖动才会被触发。
_mouseMove: function (event) {
    // IE mouseup check - mouseup happened when mouse was out of window
    if ($.ui.ie && (!document.documentMode || document.documentMode < 9) && !event.button) {
        return this._mouseUp(event);
    }

    if (this._mouseStarted) {
        this._mouseDrag(event);
        return event.preventDefault();
    }

    if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
        this._mouseStarted =
            (this._mouseStart(this._mouseDownEvent, event) !== false);
        (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
    }

    return !this._mouseStarted;
}

下一个方法将创建助手(元素的克隆),在元素中设置一些CSS,并返回true(我们期望的值),除非this._trigger("start", event)返回false。
_mouseStart: function(event) {
    var o = this.options;
    //Create and append the visible helper
    this.helper = this._createHelper(event);
    this.helper.addClass("ui-draggable-dragging");
    //Cache the helper size
    this._cacheHelperProportions();
    //If ddmanager is used for droppables, set the global draggable
    if($.ui.ddmanager) {
        $.ui.ddmanager.current = this;
    }

    /*
     * - Position generation -
     * This block generates everything position related - it's the core of draggables.
     */
    //Cache the margins of the original element
    this._cacheMargins();
    //Store the helper's css position
    this.cssPosition = this.helper.css( "position" );
    this.scrollParent = this.helper.scrollParent();
    this.offsetParent = this.helper.offsetParent();
    this.offsetParentCssPosition = this.offsetParent.css( "position" );
    //The element's absolute position on the page minus margins
    this.offset = this.positionAbs = this.element.offset();
    this.offset = {
        top: this.offset.top - this.margins.top,
        left: this.offset.left - this.margins.left
    };
    //Reset scroll cache
    this.offset.scroll = false;
    $.extend(this.offset, {
        click: { //Where the click happened, relative to the element
            left: event.pageX - this.offset.left,
            top: event.pageY - this.offset.top
        },
        parent: this._getParentOffset(),
        relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
    });
    //Generate the original position
    this.originalPosition = this.position = this._generatePosition(event);
    this.originalPageX = event.pageX;
    this.originalPageY = event.pageY;
    //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
    (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
    //Set a containment if given in the options
    this._setContainment();
    //Trigger event + callbacks
    if(this._trigger("start", event) === false) {
        this._clear();
        return false;
    }
    //Recache the helper size
    this._cacheHelperProportions();
    //Prepare the droppable offsets
    if ($.ui.ddmanager && !o.dropBehaviour) {
        $.ui.ddmanager.prepareOffsets(this, event);
    }
    this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
    //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
    if ( $.ui.ddmanager ) {
        $.ui.ddmanager.dragStart(this, event);
    }

    return true;
}

以下是第一个_trigger,它来自拖动组件。
_trigger: function (type, event, ui) {
        ui = ui || this._uiHash();
        $.ui.plugin.call(this, type, [event, ui]);
        //The absolute position has to be recalculated after plugins
        if(type === "drag") {
            this.positionAbs = this._convertPositionTo("absolute");
        }
        return $.Widget.prototype._trigger.call(this, type, event, ui);
    }

此时,结果将调用另一个触发器方法(这次来自$.Widget),这就是我们遇到问题的地方。

 _trigger: function (type, event, data) {
    var prop, orig,
        callback = this.options[type];

    data = data || {};
    event = $.Event(event);
    event.type = (type === this.widgetEventPrefix ?
        type :
        this.widgetEventPrefix + type).toLowerCase();
    // the original event may come from any element
    // so we need to reset the target on the new event
    event.target = this.element[0];

    // copy original event properties over to the new event
    orig = event.originalEvent;
    if (orig) {
        for (prop in orig) {
            if (!(prop in event)) {
                event[prop] = orig[prop];
            }
        }
    }
    return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false || event.isDefaultPrevented());
}

return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false || event.isDefaultPrevented());

我们的问题正好出现在这一行。更具体地说,在event.isDefaultPrevented()之前的||操作。 当引入Hammer时,方法event.isDefaultPrevented()的结果是true,一旦在返回前被拒绝,则最终值将为false。(如果没有引入Hammer,则event.isDefaultPrevented()按预期返回false。) 回到我们的_mouseMove()函数,它不会调用_mouseDrag()方法而是会调用_mouseUp()方法。你可以看到它将解除绑定事件并调用_mouseStop()方法。
_mouseUp: function (event) {
    $(document)
        .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
        .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);

    if (this._mouseStarted) {
        this._mouseStarted = false;

        if (event.target === this._mouseDownEvent.target) {
            $.data(event.target, this.widgetName + ".preventClickEvent", true);
        }

        this._mouseStop(event);
    }

    return false;
}

如果您将OR(||)运算符更改为AND(&&),它将正常工作。当然,这不是一个小的变化,我已经测试过了,直到现在我还没有发现任何问题。该行将如下所示:

return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false && event.isDefaultPrevented());

就像我说的那样,这并非100%安全,但直到现在,我没有发现保留 || 而不使用 && 的理由。我将继续测试几天。 此外,我已向 jQuery UI 的主要开发者发送了电子邮件询问此事。


开发人员有回复你吗? - rickdmer
1
不幸的是,我发了两封电子邮件,但没有得到任何回复。然而,我保留了这个更改,到目前为止我没有发现任何问题。 - André Junges
因为其炫酷、努力和精力充沛的表现而获得赞同。 - Ahmad Alfy
我在这个解决方案中遇到了一个问题。在我们的情况下,当锤子不活跃时(在非触摸设备上),这段代码将阻止可拖动的开始回调正确工作。具体而言,如果在开始回调中返回false,则现在除非调用event.preventDefault,否则它将被忽略。 - terrinecold
你正在使用哪个版本的jQuery、jQueryUI和hammerJS? - André Junges

1
使用同构 SmartClient 时我遇到了相似的问题。 我通过处理 start 事件并将 isDefaultPrevented 重置为 false 来解决它。
$(element).draggable({
  start: function (event, ui) {
    event.isDefaultPrevented = function () { return false; }
  }
});

这对我有用,但必须使用“拖动”回调而不是“开始”。 - DaveB

0

我曾经遇到同样的问题,并在github上找到了这个问题。

使用事件委托的解决方案对我很有效:

$(document).hammer().on('touch transform transformend', '#main', function() {...});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接