2010-08-19 10 views
35

W ramach eksperymentu utworzyłem kilka elementów div i obróciłem je za pomocą CSS3.Webkit i jQuery draggable jumping

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

Ja następnie losowo je stylizowałem i robiłem je przeciągalnym przez 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(); 

Prace przeciąganie, ale ja zauważyć nagły skok podczas przeciągania div jest tylko w przeglądarkach WebKit, a wszystko jest w porządku w Firefoksie.

Jeśli usuniemy styl : absolutny, "skakanie" jest jeszcze gorsze. Pomyślałem, że może być różnica w pochodzeniu transformacji między webkitem a gekonem, ale domyślnie oba są w środku elementu.

Wyszukiwanie już nastąpiło, ale pojawiły się tylko wyniki dotyczące pasków przewijania lub list sortowalnych.

Oto działająca wersja demonstracyjna mojego problemu. Spróbuj wyświetlić go zarówno w Safari/Chrome i Firefox. http://jsbin.com/ucehu/

Czy to błąd w webkicie lub w jaki sposób przeglądarki renderują webkit?

+0

widzę to samo rzecz (a także w Operze). Czy kiedykolwiek znalazłeś rozwiązanie/obejście? – T4NK3R

Odpowiedz

33

Jest to wynikiem zależności draggable od funkcji jquery offset() i użycia offset() macierzystej funkcji js getBoundingClientRect(). Ostatecznie jest to problem związany z rdzeniem jquery, który nie kompensuje niespójności związanych z getBoundingClientRect(). Wersja getBoundingClientRect() Firefoksa ignoruje transformacje css3 (obrót), podczas gdy chrome/safari (webkit) nie.

here jest ilustracją problemu.

hacky obejście:

zastąpić idąc jquery.ui.draggable.js


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

z


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

i wreszcie monkeypatched wersję swojego jsbin.

+1

i jest już otwarty problem na ten temat: http://bugs.jquery.com/ticket/8362 –

+0

Dzięki za poprawkę, działa idealnie. –

+0

Nie działa dla mnie (ani dłużej naprawić poniżej). Ale obejście polega na obróceniu wewnętrznego obiektu i przeciągnięciu jego opakowania macierzystego. W ten sposób pozycja jest zupełnie niezwiązana z rotacją. –

5

odpowiedź David Wick był bardzo pomocny ... dzięki ... tu oznaczonych tą samą obejście dla resizeable, ponieważ ma ten sam problem:

poszukiwania następujących w jquery.ui.resizable.js

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

i zastąpić:

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

David Wick jest prawo o ogólnym kierunku wyżej, ale obliczeniowej właściwej COOR dinates jest o wiele bardziej zaangażowany niż to.Oto dokładniejsze łata małpa, na podstawie MIT licencjonowany kod Firebug, który powinien pracować w znacznie więcej sytuacji, gdy masz skomplikowany DOM:

Zamiast wymienić:

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

z mniej hacky (koniecznie wszystko, trzeba przewinąć):

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

To wstyd, że DOM nie ujawnia tych obliczeń natywnie.

+0

Wiem, że to stare pytanie, ale mam nadzieję na jakąś pomoc. Miałem podobny problem i użyłem zarówno twojego kodu, jak i tego, który przesłał David Wick. Działają jak czar za pierwszym razem, gdy przeciągam obiekt, ale wtedy przeskakiwanie trwa, po tym jak je upuszczę i znów zacznie się ciągnąć. @ecmanaut Czy wiesz, jak to naprawić? – oneday

11

@ecmanaut: świetne rozwiązanie. Dziękuję za Twój wysiłek. Aby pomóc innym, zmieniłem twoje rozwiązanie w poprawkę małpy. Skopiuj poniższy kod do pliku. Dołączyć plik po załadowaniu jQuery ui.js następująco:

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

Oto kod skopiować/wkleić do 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

To działało świetnie. Dzięki. – Nacho

+3

Wystąpił błąd: Uncaught TypeError: Nie można odczytać właściwości "offset" niezdefiniowanego jquery-ui.js: 1298 – AllisonC

+0

To samo tutaj - Zastanawiam się, czy ktoś wymyślił aktualizację powyższego. : '- ( – JDR

1

Musisz ustawić kontenera nadrzędnego elementu Draggable do "position: relative".

25

Rysuję obraz, aby wskazać przesunięcie po obrocie w różnych przeglądarkach jako odpowiedź @David Wick.

offset after rotate

Oto kod naprawić, jeśli nie chcesz patcha lub modyfikować 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; 
     } 
    }); 
}); 

lub można zobaczyć demo

+0

To działa jak zaklęcie, po prostu brakuje jednego nawiasu po przeciągnięciu: funkcja dziękuję – Alfonso

+0

@Alfonso Dziękuję za przypomnienie –

+1

+1 Za używanie Lena – chiliNUT

2

użyłem wiele rozwiązań do przeciągania działa poprawnie. ALE, nadal reagował źle na zrzut strefy (tak, jakby nie był obracany). Rozwiązaniem jest użycie kontenera nadrzędnego, który jest względnie pozycjonowany.

To zaoszczędziło mi bardzo dużo czasu.

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



.rect-container { 
    position:relative; 
} 

pełne rozwiązanie tutaj (to nie ode mnie): http://jsfiddle.net/Sp6qa/2/

także Badałem wiele. I tak po prostu, jQuery nie ma żadnych planów zmiany obecnego zachowania w przyszłości. Wszystkie przesłane zgłoszenia dotyczące tego tematu zostały zamknięte. Zacznij więc od posiadania kont nadrzędnych, które są pozycjonowane względnie. Działa jak urok i powinien być przyszłościowy.

2

wolę tego obejścia, gdyż zachowuje oryginalną obsługi
usuwa przekształcić następnie przywraca ona

$(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 (zaczęło się od @Liao San-Kai jsbin)

Powiązane problemy