2010-08-19 46 views
35

作爲一個實驗,我創建了幾個div並使用CSS3旋轉它們。Webkit和jQuery可拖動跳躍

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

拖動工作,但我注意到突然跳躍,而拖動div只在webkit瀏覽器,而一切都很好的Firefox。

如果我刪除位置:絕對風格,「跳躍」更糟。我認爲webkit和gecko之間的轉換原點可能有所不同,但默認情況下它們都位於元素的中心。

我已經搜索了周圍,但只有想出滾動條或可排序列表的結果。

這是我的問題的工作演示。嘗試在Safari/Chrome和Firefox中查看它。 http://jsbin.com/ucehu/

這是webkit中的bug還是瀏覽器呈現webkit的方式?

+0

我看到了同樣的事情(也在Opera中)。你有沒有找到解決方案/解決方法? – T4NK3R 2011-02-23 12:18:17

回答

33

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

here是對該問題的說明。

哈克解決方法:

替換掉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的monkeypatched版本以下。

+1

並且這方面也存在一個未解決的問題:http://bugs.jquery.com/ticket/8362 – 2011-06-17 18:54:50

+0

感謝您的修復,完美地工作。 – 2012-09-06 14:19:28

+0

沒有爲我工作(不再修復下面)。但解決方法是旋轉內部對象,並拖動他的父包裝。這樣的位置是完全無關的旋轉.. – 2012-11-17 03:42:18

5

大衛威克的答案是非常有益的...謝謝... 這裏我編寫了調整大小相同的解決方法,因爲它有同樣的問題:

搜索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; 
21

大衛維克是正確的關於一般方向的上方,但在計算權COOR晚餐是比這更多的參與。這裏有一個更準確的猴子補丁,基於MIT許可Firebug的代碼,它應該工作在你有一個複雜的DOM更爲情況:

而是替代:

 //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 = 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不公開這些計算本身。

+0

我知道這是一個老問題,但我希望得到一些幫助。我遇到了類似的問題,我使用了代碼和David Wick提交的代碼。當我拖動物體時,他們第一次像魅力一樣工作,但是在我放下物體並再次拖動之後繼續跳躍。你知道如何解決它嗎? – oneday 2014-09-04 12:28:19

11

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

這很好。謝謝。 – Nacho 2012-11-09 13:07:35

+3

我得到一個錯誤:未捕獲TypeError:無法讀取未定義的屬性'偏移量'jquery-ui.js:1298 – AllisonC 2015-01-07 16:04:08

+0

同樣在這裏 - 我想知道是否有人有上面的更新。 :' - ( – JDR 2016-01-24 00:52:38

1

必須設置可拖動元素的父容器到「位置:相對」。

25

我在@David Wick的答案中繪製圖像以指示在不同瀏覽器上旋轉後的偏移量。

offset after rotate

下面的代碼來解決,如果你不想打補丁或修改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

+0

它的工作原理就像一個魅力,你只是在拖動功能後丟失了一個支架謝謝 – Alfonso 2013-08-15 11:01:46

+0

@Alfonso謝謝你提醒我 – 2013-08-21 07:38:45

+1

+1對於使用Lena – chiliNUT 2013-09-27 18:27:45

2

我用了很多解決方案讓拖動工作正常。但是,它仍然對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沒有任何計劃在將來改變當前的行爲。所有提交的有關該主題的門票都已關閉。所以,剛開始的時候有一個相對定位的父容器。它的作用像一個魅力,應該是未來的保障。

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(從@Liao聖凱jsbin開始)