【问题标题】:Webkit and jQuery draggable jumpingWebkit 和 jQuery 可拖动跳跃
【发布时间】:2011-04-01 05:25:18
【问题描述】:

作为一个实验,我创建了几个 div 并使用 CSS3 旋转它们。

    .items { 
        position: absolute;
        cursor: pointer;
        background: #FFC400;
        -moz-box-shadow: 0px 0px 2px #E39900;
        -webkit-box-shadow: 1px 1px 2px #E39900; 
        box-shadow: 0px 0px 2px #E39900;
        -moz-border-radius: 2px; 
        -webkit-border-radius: 2px;
        border-radius: 2px;
    }

然后我随机设置它们的样式并通过 jQuery 使它们可拖动。

    $('.items').each(function() {
        $(this).css({
            top: (80 * Math.random()) + '%',
            left: (80 * Math.random()) + '%',
            width: (100 + 200 * Math.random()) + 'px',
            height: (10 + 10 * Math.random()) + 'px',
            '-moz-transform': 'rotate(' + (180 * Math.random()) + 'deg)',
            '-o-transform': 'rotate(' + (180 * Math.random()) + 'deg)',
            '-webkit-transform': 'rotate(' + (180 * Math.random()) + 'deg)',
        });
    });

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

拖动工作正常,但我注意到仅在 webkit 浏览器中拖动 div 时突然跳跃,而在 Firefox 中一切正常。

如果我删除 position: absolute 样式,“跳跃”会更糟。我认为 webkit 和 gecko 之间的变换原点可能存在差异,但默认情况下它们都位于元素的中心。

我已经搜索过了,但只找到了关于滚动条或可排序列表的结果。

这是我的问题的工作演示。尝试在 Safari/Chrome 和 Firefox 中查看它。 http://jsbin.com/ucehu/

这是 webkit 中的错误还是浏览器如何呈现 webkit?

【问题讨论】:

  • 我看到了同样的事情(也在 Opera 中)。你有没有找到解决方案/解决方法?

标签: javascript jquery css webkit


【解决方案1】:

作为@David Wick 的回答,我在不同的浏览器上绘制了一个图像来指示旋转后的偏移量。

如果您不想修补或修改 jquery.ui.draggable.js,这是要修复的代码

$(document).ready(function () {
    var recoupLeft, recoupTop;
    $('#box').draggable({
        start: function (event, ui) {
            var left = parseInt($(this).css('left'),10);
            left = isNaN(left) ? 0 : left;
            var top = parseInt($(this).css('top'),10);
            top = isNaN(top) ? 0 : top;
            recoupLeft = left - ui.position.left;
            recoupTop = top - ui.position.top;
        },
        drag: function (event, ui) {
            ui.position.left += recoupLeft;
            ui.position.top += recoupTop;
        }
    });
});

或者你可以看到demo

【讨论】:

  • 它就像一个魅力。在 drag : 函数之后,您只是缺少一个括号。谢谢
  • 谢谢,这拯救了我的一天。
  • 谢谢你,你解决了我最烦人的问题之一!
  • 完美,这个答案上面的monkeypatch一直有效,直到最近版本的UI(v10)对我来说......这是一个很好的解决方案,谢谢!
  • 太棒了,并且 +1 不必修改任何 jQuery 代码。
【解决方案2】:

我更喜欢这种解决方法,因为它保留了原始处理程序
它删除转换然后恢复它

$(document).ready(function(){

    // backup original handler
    var _mouseStart = $.ui.draggable.prototype._mouseStart;

    $.ui.draggable.prototype._mouseStart = function(event) {

        //remove the transform
        var transform = this.element.css('transform');
        this.element.css('transform', 'none');

        // call original handler
        var result = _mouseStart.call(this, event);

        //restore the transform
        this.element.css('transform', transform);

        return result;
    };
});

demo(从@廖三凯jsbin开始)

【讨论】:

  • 这太棒了!我多年来一直在寻找这个;-)
【解决方案3】:

我使用了很多解决方案来让拖动正常工作。但是,它仍然对 dropzone 做出错误的反应(就像它没有旋转一样)。解决方案实际上是使用相对定位的父容器。

这为我节省了很多时间。

<div id="drawarea">
    <div class="rect-container h">
        <div class="rect"></div>
    </div>
</div> 



.rect-container {
    position:relative; 
}

这里的完整解决方案(不是来自我): http://jsfiddle.net/Sp6qa/2/

我也研究了很多。就像这样,jQuery 没有任何计划在未来改变当前的行为。所有提交的关于该主题的票都已关闭。因此,只需从拥有相对定位的父容器开始。它就像一个魅力,应该是面向未来的。

【讨论】:

    【解决方案4】:

    大卫威克的回答非常有帮助......谢谢...... 在这里,我为可调整大小编写了相同的解决方法,因为它有同样的问题:

    jquery.ui.resizable.js中搜索以下内容

    var o = this.options, iniPos = this.element.position(), el = this.element;
    

    并替换为:

    var o = this.options, iniPos = {top:this.element[0].offsetTop,left:this.element[0].offsetLeft}, el = this.element;
    

    【讨论】:

      【解决方案5】:

      您必须将可拖动元素的父容器设置为“位置:相对”。

      【讨论】:

        【解决方案6】:

        @ecmanaut:很好的解决方案。感谢您的努力。为了帮助其他人,我将您的解决方案变成了猴子补丁。将以下代码复制到文件中。加载jquery-ui.js后包含文件如下:

        <script src="javascripts/jquery/jquery.js"></script>
        <script src="javascripts/jquery/jquery-ui.js"></script>
        
        <!-- the file containing the monkey-patch to draggable -->
        <script src="javascripts/jquery/patch_draggable.js"></script>
        

        这是复制/粘贴到 patch_draggable.js 中的代码:

        function monkeyPatch_mouseStart() {
             // don't really need this, but in case I did, I could store it and chain
             var oldFn = $.ui.draggable.prototype._mouseStart ;
             $.ui.draggable.prototype._mouseStart = function(event) {
        
                    var o = this.options;
        
                   function getViewOffset(node) {
                      var x = 0, y = 0, win = node.ownerDocument.defaultView || window;
                      if (node) addOffset(node);
                      return { left: x, top: y };
        
                      function getStyle(node) {
                        return node.currentStyle || // IE
                               win.getComputedStyle(node, '');
                      }
        
                      function addOffset(node) {
                        var p = node.offsetParent, style, X, Y;
                        x += parseInt(node.offsetLeft, 10) || 0;
                        y += parseInt(node.offsetTop, 10) || 0;
        
                        if (p) {
                          x -= parseInt(p.scrollLeft, 10) || 0;
                          y -= parseInt(p.scrollTop, 10) || 0;
        
                          if (p.nodeType == 1) {
                            var parentStyle = getStyle(p)
                              , localName   = p.localName
                              , parent      = node.parentNode;
                            if (parentStyle.position != 'static') {
                              x += parseInt(parentStyle.borderLeftWidth, 10) || 0;
                              y += parseInt(parentStyle.borderTopWidth, 10) || 0;
        
                              if (localName == 'TABLE') {
                                x += parseInt(parentStyle.paddingLeft, 10) || 0;
                                y += parseInt(parentStyle.paddingTop, 10) || 0;
                              }
                              else if (localName == 'BODY') {
                                style = getStyle(node);
                                x += parseInt(style.marginLeft, 10) || 0;
                                y += parseInt(style.marginTop, 10) || 0;
                              }
                            }
                            else if (localName == 'BODY') {
                              x += parseInt(parentStyle.borderLeftWidth, 10) || 0;
                              y += parseInt(parentStyle.borderTopWidth, 10) || 0;
                            }
        
                            while (p != parent) {
                              x -= parseInt(parent.scrollLeft, 10) || 0;
                              y -= parseInt(parent.scrollTop, 10) || 0;
                              parent = parent.parentNode;
                            }
                            addOffset(p);
                          }
                        }
                        else {
                          if (node.localName == 'BODY') {
                            style = getStyle(node);
                            x += parseInt(style.borderLeftWidth, 10) || 0;
                            y += parseInt(style.borderTopWidth, 10) || 0;
        
                            var htmlStyle = getStyle(node.parentNode);
                            x -= parseInt(htmlStyle.paddingLeft, 10) || 0;
                            y -= parseInt(htmlStyle.paddingTop, 10) || 0;
                          }
        
                          if ((X = node.scrollLeft)) x += parseInt(X, 10) || 0;
                          if ((Y = node.scrollTop))  y += parseInt(Y, 10) || 0;
                        }
                      }
                    }
        
        
                        //Create and append the visible helper
                        this.helper = this._createHelper(event);
        
                        //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();
        
                        //The element's absolute position on the page minus margins
                    this.offset = this.positionAbs = getViewOffset(this.element[0]);
                        this.offset = {
                            top: this.offset.top - this.margins.top,
                            left: this.offset.left - this.margins.left
                        };
        
                        $.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
                        if(o.containment)
                            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.helper.addClass("ui-draggable-dragging");
                        //JWL: Hier vindt de jump plaats
                        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;
        
             };
        
         }
        monkeyPatch_mouseStart();
        

        【讨论】:

        • 我收到一个错误:未捕获的类型错误:无法读取未定义 jquery-ui.js:1298 的属性“偏移量”
        • 这里也一样 - 我想知道是否有人提出了上述更新。 :'-(
        【解决方案7】:

        David Wick 对上述大方向的看法是正确的,但计算正确的坐标比这要复杂得多。这是一个更准确的猴子补丁,基于 MIT 许可的 Firebug 代码,它应该适用于更多具有复杂 DOM 的情况:

        改为替换:

         //页面上元素的绝对位置减去边距
            this.offset = this.positionAbs = this.element.offset();

        使用较少的hacky(确保获得全部内容;您需要滚动):

         //页面上元素的绝对位置减去边距
            this.offset = this.positionAbs = getViewOffset(this.element[0]);
        
            函数getViewOffset(节点){
              var x = 0, y = 0, win = node.ownerDocument.defaultView ||窗户;
              if (node) addOffset(node);
              返回{左:x,上:y};
        
              函数getStyle(节点){
                返回节点.currentStyle || // IE
                       win.getComputedStyle(node, '');
              }
        
              函数addOffset(节点){
                var p = node.offsetParent,样式,X,Y;
                x += parseInt(node.offsetLeft, 10) || 0;
                y += parseInt(node.offsetTop, 10) || 0;
        
                如果(p){
                  x -= parseInt(p.scrollLeft, 10) || 0;
                  y -= parseInt(p.scrollTop, 10) || 0;
        
                  如果(p.nodeType == 1){
                    var parentStyle = getStyle(p)
                      , 本地名称 = p.本地名称
                      , 父级 = 节点.parentNode;
                    if (parentStyle.position != 'static') {
                      x += parseInt(parentStyle.borderLeftWidth, 10) || 0;
                      y += parseInt(parentStyle.borderTopWidth, 10) || 0;
        
                      if (localName == 'TABLE') {
                        x += parseInt(parentStyle.paddingLeft, 10) || 0;
                        y += parseInt(parentStyle.paddingTop, 10) || 0;
                      }
                      否则 if (localName == 'BODY') {
                        样式 = 获取样式(节点);
                        x += parseInt(style.marginLeft, 10) || 0;
                        y += parseInt(style.marginTop, 10) || 0;
                      }
                    }
                    否则 if (localName == 'BODY') {
                      x += parseInt(parentStyle.borderLeftWidth, 10) || 0;
                      y += parseInt(parentStyle.borderTopWidth, 10) || 0;
                    }
        
                    而(p!=父){
                      x -= parseInt(parent.scrollLeft, 10) || 0;
                      y -= parseInt(parent.scrollTop, 10) || 0;
                      父 = 父节点;
                    }
                    addOffset(p);
                  }
                }
                别的 {
                  if (node.localName == 'BODY') {
                    样式 = 获取样式(节点);
                    x += parseInt(style.borderLeftWidth, 10) || 0;
                    y += parseInt(style.borderTopWidth, 10) || 0;
        
                    var htmlStyle = getStyle(node.parentNode);
                    x -= parseInt(htmlStyle.paddingLeft, 10) || 0;
                    y -= parseInt(htmlStyle.paddingTop, 10) || 0;
                  }
        
                  if ((X = node.scrollLeft)) x += parseInt(X, 10) || 0;
                  if ((Y = node.scrollTop)) y += parseInt(Y, 10) || 0;
                }
              }
            }

        很遗憾 DOM 没有原生地公开这些计算。

        【讨论】:

        • 我知道这是一个老问题,但我希望能得到一些帮助。我有一个类似的问题,我使用了你的代码和大卫威克提交的代码。当我第一次拖动对象时,它们就像一个魅力,但在我放下它并再次拖动后,跳跃继续。 @ecmanaut 你知道怎么解决吗?
        【解决方案8】:

        这是由于draggable依赖jquery offset()函数和offset()使用原生js函数getBoundingClientRect()的结果。最终,这是 jquery 核心无法补偿与getBoundingClientRect() 相关的不一致的问题。 Firefox 的 getBoundingClientRect() 版本会忽略 css3 转换(旋转),而 chrome/safari (webkit) 则不会。

        here 是该问题的说明。

        一个 hacky 解决方法:

        替换jquery.ui.draggable.js中的关注

        
        //The element's absolute position on the page minus margins
        this.offset = this.positionAbs = this.element.offset();
        

        
        //The element's absolute position on the page minus margins
        this.offset = this.positionAbs = { top: this.element[0].offsetTop, 
                                           left: this.element[0].offsetLeft };
        

        最后是你的 jsbin 的猴子补丁版本。

        【讨论】:

        • 并且已经有一个未解决的问题:bugs.jquery.com/ticket/8362
        • 对我没有用(下面不再修复)。但解决方法是旋转内部对象,并拖动他的父包装。这样位置与旋转完全无关..
        猜你喜欢
        • 2014-06-16
        • 2015-07-01
        • 2012-12-02
        • 1970-01-01
        • 2013-09-15
        • 2012-12-02
        • 1970-01-01
        • 1970-01-01
        • 2013-03-15
        相关资源
        最近更新 更多