簡體   English   中英

Webkit 和 jQuery 可拖動跳轉

[英]Webkit and jQuery draggable jumping

作為一個實驗,我創建了一些 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?

我畫了一個圖像來指示在不同瀏覽器上旋轉后的偏移量作為@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;
        }
    });
});

或者你可以看演示

這是draggable 依賴jquery offset()函數和offset()使用原生js 函數getBoundingClientRect() 最終,這是 jquery 核心無法補償與getBoundingClientRect()相關的不一致的問題。 Firefox 的getBoundingClientRect()版本會忽略 css3 轉換(旋轉),而 chrome/safari (webkit) 不會。

是該問題的說明。

一個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 };

最后一個monkeypatched您的版本jsbin

大衛威克關於上面的總體方向是正確的,但計算正確的坐標比這更復雜。 這是一個更准確的猴子補丁,基於 MIT 許可的 Firebug 代碼,它應該適用於您擁有復雜 DOM 的更多情況:

而是替換:

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

使用較少的hacky(確保獲得整個內容;您需要滾動):

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

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

遺憾的是 DOM 沒有在本地公開這些計算。

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

我更喜歡這種解決方法,因為它保留了原始處理程序
它刪除變換然后恢復它

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

演示(從@Liao San-Kai jsbin 開始)

大衛威克的回答非常有幫助...謝謝...在這里我為可調整大小編寫了相同的解決方法,因為它有同樣的問題:

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;

我使用了很多解決方案來使拖動正常工作。 但是,它仍然對 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 沒有任何計划在未來改變當前的行為。 所有提交的關於該主題的票都已關閉。 因此,只需從具有相對定位的父容器開始。 它就像一種魅力,應該是面向未來的。

您必須將可拖動元素的父容器設置為“位置:相對”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM