2010-08-19 8 views
35

Como experimento, creé algunos div y los giré usando CSS3.Webkit y jQuery que se puede arrastrar saltando

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

Luego los diseñé aleatoriamente y los hice arrastrables a través de 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(); 

Los trabajos de arrastre, pero estoy notando un salto repentino mientras arrastra los divs sólo en los navegadores WebKit, mientras que todo está bien en Firefox.

Si elimino la posición : estilo absoluto, el 'salto' es aún peor. Pensé que tal vez había una diferencia en el origen de la transformación entre webkit y gecko, pero ambos están en el centro del elemento de forma predeterminada.

Ya he buscado, pero solo aparecieron los resultados sobre las barras de desplazamiento o las listas ordenables.

Aquí hay una demostración funcional de mi problema. Intenta verlo en Safari/Chrome y Firefox. http://jsbin.com/ucehu/

¿Se trata de un error en el sitio web o de cómo los navegadores procesan Webkit?

+0

que estoy viendo la misma cosa (y también en Opera). ¿Alguna vez encontró una solución/solución alternativa? – T4NK3R

Respuesta

33

Esto es resultado de la dependencia de la función jquery offset() de arrastrar y el uso offset() de la función nativa js getBoundingClientRect(). En última instancia, se trata de un problema con el núcleo jquery que no compensa las incoherencias asociadas con getBoundingClientRect(). La versión de Firefox de getBoundingClientRect() ignora las transformaciones css3 (rotación) mientras que cromo/safari (webkit) no lo hace.

here es una ilustración del problema.

Una solución hacky:

replace siguiente en jquery.ui.draggable.js


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

con


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

y finalmente una versión de su monkeypatched jsbin.

+1

y ya hay un tema abierto para esto también: http://bugs.jquery.com/ticket/8362 –

+0

Gracias por la corrección, funciona perfectamente. –

+0

No funcionó para mí (ni más arreglo a continuación). Pero la solución consiste en rotar el objeto interno y arrastrar su envoltura primaria. De esa forma, la posición no tiene relación con la rotación. –

5

la respuesta de David Wick fue muy útil ... gracias ... aquí i codificado la misma solución para el tamaño variable, ya que tiene el mismo problema:

búsqueda del siguiente en jquery.ui.resizable.js

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

y reemplazar con:

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

David Wick es correcta acerca de la dirección general anterior, pero el cálculo de la derecha koor Dinates es mucho más complicado que eso.Aquí está un parche mono más exacto, basado en el MIT licencia de código Firebug, que debe trabajar en muchas más situaciones donde se tiene una de DOM complejo:

lugar reemplace:

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

con los menos hacky (asegúrese de obtener toda la cosa, lo necesitará para desplazarse):

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

Es una pena que el DOM no expone estos cálculos de forma nativa.

+0

Sé que es una vieja pregunta, pero espero algo de ayuda. Tuve un problema similar y utilicé tanto su código como el que David Wick envió. Funcionan como un amuleto la primera vez cuando arrastro el objeto, pero luego el salto continúa después de que lo suelte y arrastre de nuevo. @ecmanaut ¿sabes cómo solucionarlo? – oneday

11

@ecmanaut: Gran solución. Gracias por tus esfuerzos. Para ayudar a otros, convertí tu solución en un parche de mono. Copie el código a continuación en un archivo. Incluir el archivo después de cargar jquery-ui.js de la siguiente manera:

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

Aquí está el código para copiar/pegar en 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

Esto funcionó muy bien. Gracias. – Nacho

+3

Recibí un error: TypeError no detectado: No se puede leer la propiedad 'offset' de undefined jquery-ui.js: 1298 – AllisonC

+0

Lo mismo aquí: me pregunto si alguien ha tenido una actualización de lo anterior. : '- ( – JDR

1

Usted tiene que fijar el contenedor padre del elemento arrastrable a "posición: relativa".

25

debo dibujar una imagen para indicar el desplazamiento después de girar sobre diferentes navegadores como la respuesta de @ David Wick.

offset after rotate

Aquí está el código para arreglar si usted no quiere parche o modificar 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; 
     } 
    }); 
}); 

o se puede ver la demo

+0

Funciona a las mil maravillas. Solo le falta un soporte después de la función de arrastre. Gracias – Alfonso

+0

@ Alfonso Gracias por recordarme –

+1

+1 Por usar Lena – chiliNUT

2

he usado una muchas de las soluciones para lograr que el arrastre funcione correctamente. PERO, todavía reaccionó mal a una zona de caída (como si no hubiera sido rotada). La solución realmente es usar un contenedor principal que esté posicionado de manera relativa.

Esto me salvó tan mucho tiempo.

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



.rect-container { 
    position:relative; 
} 

solución completa aquí (que no es de mí): http://jsfiddle.net/Sp6qa/2/

También investigó mucho. Y es así, jQuery no tiene planes de cambiar ese comportamiento actual en el futuro. Todos los tickets enviados sobre ese tema se cerraron. Por lo tanto, empiece con tener contenedores padres que estén en posición relativa. Funciona como un encanto y debe ser a prueba de futuro.

2

prefiero esta solución ya que evita que el controlador original de
Se elimina la transformada luego lo restaura

$(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 (partido de San @Liao-Kai jsbin)

Cuestiones relacionadas