48 votos

jQuery Arrastrar y cambiar el tamaño con CSS Transformar la Escala de

Estoy aplicando un CSS transformar (y el navegador específico -webkit, -o, etc):

transformar: matriz de(0.5 , 0 , 0, 0.5, 0 , 0);

a un div, a continuación, utilizando jQuery draggable() y de tamaño variable() plugins en los niños de dicho div.

El problema que tenía era que al arrastrar o cambiar el tamaño de los elementos secundarios, la alteración de jQuery eran de "sincronización" con el ratón por un factor igual a la escala que se aplica.

He encontrado una solución en stackoverflow (aunque yo estúpidamente no los favoritos y ahora no puedo encontrarlo....) la que se sugiere la revisión de los plugins, y funcionó de maravilla. Fue a lo largo de estas líneas:

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;

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

    //PATCH CODE
    this.offset = this.positionAbs = getViewOffset(this.element[0]);
    //END

    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
    if(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");
    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) $.ui.ddmanager.dragStart(this, event);

    return true;
  }
 }

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;
    }
  }
}
var isNumber = function(value) {
  return !isNaN(parseInt(value, 10));
};

He hecho mis propios cambios, tales como (se puede ver en el 6-7 líneas de la multiplicación de los movimientos de un "factor de escala"):

 $.ui.draggable.prototype._generatePosition = function(event) {
    var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
    var pageX = event.pageX;
    var pageY = event.pageY;
    //PATCH CODE
    if($(this.element[0]).hasClass('item')){
        pageY = this.originalPageY + ((pageY - this.originalPageY)*(1/$.viewbox.foreground.scale));
        pageX = this.originalPageX + ((pageX - this.originalPageX)*(1/$.viewbox.foreground.scale));
    }
    //END
    /*
     * - Position constraining -
     * Constrain the position to a mix of grid, containment.
     */

    if(this.originalPosition) { //If we are not dragging yet, we won't check for options

      if(this.containment) {
        if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
        if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
        if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
        if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
      }

      if(o.grid) {
        var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
        pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;

        var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
        pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
      }
    }
    return {
      top: (
        pageY                               // The absolute mouse position
        - this.offset.click.top                         // Click offset (relative to the element)
        - this.offset.relative.top                        // Only for relative positioned nodes: Relative offset from element to offset parent
        - this.offset.parent.top                        // The offsetParent's offset without borders (offset + border)
        + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
      ),
      left: (
        pageX                               // The absolute mouse position
        - this.offset.click.left                        // Click offset (relative to the element)
        - this.offset.relative.left                       // Only for relative positioned nodes: Relative offset from element to offset parent
        - this.offset.parent.left                       // The offsetParent's offset without borders (offset + border)
        + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
      )
    };

  }

Así que muchas gracias a quien sugirió que.

Entonces, mi pregunta! Alguien ha encontrado una buena manera de tener la posibilidad de arrastrar/eventos de tamaño variable dentro de una escala de elemento que no requieren revisiones de jQuery? He buscado en google, y esta es la mejor solución que he podido encontrar. ¿Alguien sabe de alternativa a jquery que quizás funciona bajo estas condiciones, con CSS transforma?

Muchas gracias por las respuestas.

41voto

Gung Foo Puntos 6050

Ha sido un tiempo desde que esta pregunta se la hicieron. He encontrado (en realidad creado) una respuesta. Todo lo que se requiere es la configuración de los controladores callback. No hay edición de jquery-ui necesario!

Nota: zoomScale en este ejemplo es una variable global y la transformación se establece el uso de animar (ayudados por jquery.transform.js) así:

target.animate({
    transform: 'scale(' + zoomScale + ')'
});

Echa un vistazo a esto:

transformar la escala() revisión de tamaño variable:

$(this).resizable({
    minWidth: -(contentElem.width()) * 10,  // these need to be large and negative
    minHeight: -(contentElem.height()) * 10, // so we can shrink our resizable while scaled
    resize: function(event, ui) {

        var changeWidth = ui.size.width - ui.originalSize.width; // find change in width
        var newWidth = ui.originalSize.width + changeWidth / zoomScale; // adjust new width by our zoomScale

        var changeHeight = ui.size.height - ui.originalSize.height; // find change in height
        var newHeight = ui.originalSize.height + changeHeight / zoomScale; // adjust new height by our zoomScale

        ui.size.width = newWidth;
        ui.size.height = newHeight;

    }
});

transformar la escala (a) revisión por draggable:

$(this).draggable({
    handle: '.drag-handle',
    start: function(event, ui) {
        ui.position.left = 0;
        ui.position.top = 0;
    },
    drag: function(event, ui) {

        var changeLeft = ui.position.left - ui.originalPosition.left; // find change in left
        var newLeft = ui.originalPosition.left + changeLeft / (( zoomScale)); // adjust new left by our zoomScale

        var changeTop = ui.position.top - ui.originalPosition.top; // find change in top
        var newTop = ui.originalPosition.top + changeTop / zoomScale; // adjust new top by our zoomScale

        ui.position.left = newLeft;
        ui.position.top = newTop;

    }
});

Déjeme saber si usted encuentra cualquier problema o nuevas mejoras en este. :)

Referencia: jQuery-UI ajustable/puede arrastrarse con transform: scale() conjunto de

3voto

paullth Puntos 700

Mi propio "respuesta" fue en esta ocasión fue la adaptación de jQuery UI que se pueden arrastrar para hacer un aparte de la interacción, llamado "traggable".

https://github.com/paullth/JS-libs

http://jsfiddle.net/paul_herve/ws27C/4/

Todavía me gustaría conocer las alternativas...

2voto

gal Puntos 26

Otro enfoque sería agregar un plugin que compensar la transformación ( recuerde agregar "transformar : true" para el plugin de inicialización.

La interfaz de usuario.la posibilidad de arrastrar deben pasar a través de un inversa de la matriz de la transformación en el fin de la posición del elemento en la onu-se transforma el espacio que el navegador posterior transformación en pantalla.

Para "draggable" el siguiente trabajó para mí ( jqueryui 1.10 ) ( la matriz de cálculo que he tomado de jquery.panzoom):

var Matrix = function(a, b, c, d, e, f, g, h, i) {
    if ($.type(a) === 'array') {
        this.elements = [
            +a[0], +a[2], +a[4],
            +a[1], +a[3], +a[5],
                0,     0,     1
        ];
    } else {
        this.elements = [
            a, b, c,
            d, e, f,
            g || 0, h || 0, i || 1
        ];
    }
};

Matrix.prototype = {
    /**
     * Multiply a 3x3 matrix by a similar matrix or a vector
     * @param {Matrix|Vector} matrix
     * @return {Matrix|Vector} Returns a vector if multiplying by a vector
     */
    x: function(matrix) {
        var isVector = matrix instanceof Vector;

        var a = this.elements,
            b = matrix.elements;

        if (isVector && b.length === 3) {
            // b is actually a vector
            return new Vector(
                a[0] * b[0] + a[1] * b[1] + a[2] * b[2],
                a[3] * b[0] + a[4] * b[1] + a[5] * b[2],
                a[6] * b[0] + a[7] * b[1] + a[8] * b[2]
            );
        } else if (b.length === a.length) {
            // b is a 3x3 matrix
            return new Matrix(
                a[0] * b[0] + a[1] * b[3] + a[2] * b[6],
                a[0] * b[1] + a[1] * b[4] + a[2] * b[7],
                a[0] * b[2] + a[1] * b[5] + a[2] * b[8],

                a[3] * b[0] + a[4] * b[3] + a[5] * b[6],
                a[3] * b[1] + a[4] * b[4] + a[5] * b[7],
                a[3] * b[2] + a[4] * b[5] + a[5] * b[8],

                a[6] * b[0] + a[7] * b[3] + a[8] * b[6],
                a[6] * b[1] + a[7] * b[4] + a[8] * b[7],
                a[6] * b[2] + a[7] * b[5] + a[8] * b[8]
            );
        }
        return false; // fail
    },
    /**
     * Generates an inverse of the current matrix
     * @returns {Matrix}
     */
    inverse: function() {
        var d = 1 / this.determinant(),
            a = this.elements;
        return new Matrix(
            d * ( a[8] * a[4] - a[7] * a[5]),
            d * (-(a[8] * a[1] - a[7] * a[2])),
            d * ( a[5] * a[1] - a[4] * a[2]),

            d * (-(a[8] * a[3] - a[6] * a[5])),
            d * ( a[8] * a[0] - a[6] * a[2]),
            d * (-(a[5] * a[0] - a[3] * a[2])),

            d * ( a[7] * a[3] - a[6] * a[4]),
            d * (-(a[7] * a[0] - a[6] * a[1])),
            d * ( a[4] * a[0] - a[3] * a[1])
        );
    },
    /**
     * Calculates the determinant of the current matrix
     * @returns {Number}
     */
    determinant: function() {
        var a = this.elements;
        return a[0] * (a[8] * a[4] - a[7] * a[5]) - a[3] * (a[8] * a[1] - a[7] * a[2]) + a[6] * (a[5] * a[1] - a[4] * a[2]);
    }
};

var Vector = function (x, y, z) {
    this.elements = [ x, y, z ];
};

/**
 * Get the element at zero-indexed index i
 * @param {Number} i
 */
Vector.prototype.e = Matrix.prototype.e = function(i) {

    if( this.elements[ i ] != undefined ){
        return this.elements[ i ];    
    }

    return this.elements;
};    

$.ui.plugin.add("draggable", "transform", {

    start: function( event, ui ) {

        if(!$(this).data('ui-draggable')){
            return false;
        }            

        var inst = $(this).data("ui-draggable");

        inst.matrix = new Matrix(function(matrix){

            var rmatrix = new RegExp(
                    '^matrix\\(' +
                    '(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
                    '(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
                    '(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
                    '(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
                    '(\\-?[\\d\\.e]+)' + '\\,?\\s*' +
                    '(\\-?[\\d\\.e]+)' + '\\)$'
            );                

            var matrix = rmatrix.exec( matrix );
            if (matrix) {
                matrix.shift();
            }
            return matrix || [ 1, 0, 0, 1, 0, 0 ];

        }([$(this).parents('[style*="transform"]').css('transform')]));            
    },
    drag: function( event, ui ) {

        if(!$(this).data('ui-draggable')){
            return false;
        }            

        var inst = $(this).data("ui-draggable");

        var t_pos = inst.matrix.inverse().x(new Vector(ui.position.left, ui.position.top, 0));

        ui.position.left = t_pos.e(0);
        ui.position.top = t_pos.e(1);                   

        if(inst.options.grid) {
            ui.position.left = ui.position.left - ui.position.left % inst.options.grid[0];
            ui.position.top  = ui.position.top - ui.position.top % inst.options.grid[1];                
        }

        if( inst.containment ){

            if( ui.position.left < inst.containment[0] ){
                ui.position.left = inst.containment[0];
            }

            if( ui.position.left > inst.containment[2] ){
                ui.position.left = inst.containment[2];
            }                

            if( ui.position.top < inst.containment[1] ){
                ui.position.top = inst.containment[1];
            }  

            if( ui.position.top > inst.containment[3] ){
                ui.position.top = inst.containment[3];
            }
        }
    },     
});

2voto

Guy Puntos 844

he tenido un problema similar con la transformación y terminó resolviendo con css:

transform-origin

http://www.w3schools.com/cssref/css3_pr_transform-origin.asp

has probado? tal vez pueda ayudar.

0voto

user2862304 Puntos 1

Aquí está mi solución. Algunos de los códigos de arriba no me funciona.

Tengo un dragable imagen y yo de aplicar CSS transformar rotar y escalar en la imagen. Una vez que escalar/rotar la posición de cerrado el arrastre de inicio. Para solucionarlo tengo que calcular el valor del punto de diferencia, desde la parada de inicio y arrastre de aplicar un nuevo punto de ajuste, con la diferencia de tenerse en cuenta.

var $img = $("#my-image-id");

$img.posLeft  = 0;
$img.posTop  = 0;
$img.diffLeft  = 0;
$img.diffTop  = 0;

$img.draggable({
  start: function( event, ui ) {
    //check the difference from start to stop setpoints
    $img.diffLeft  = $img.posLeft-ui.position.left;
    $img.diffTop  = $img.posTop-ui.position.top;
    //console.log('start x: '+Math.floor(ui.position.left)+' y: '+Math.floor(ui.position.top));
    //console.log('diff x: '+Math.floor($img.posLeft-ui.position.left)+' y: '+Math.floor($img.posTop-ui.position.top));
  },
  drag: function( event, ui ) {
    //fix the offset bug that is applied after CSS transform, to do that just add the value difference
    ui.position.left = ui.position.left+$img.diffLeft;
    ui.position.top = ui.position.top+$img.diffTop;
    //console.log('drag x: '+ui.position.left+' y: '+ui.position.top);
  },
  stop: function( event, ui ) {
    //save the last applied setpoints
    $img.posLeft = ui.position.left;
    $img.posTop = ui.position.top;
    //console.log('stop x: '+Math.floor(ui.position.left)+' y: '+Math.floor(ui.position.top));
  }
});

El CSS transformar origen truco no corrige el error de escala solamente. La rotación debe ser de alrededor de centro, de manera que usted desea mantener el valor predeterminado es 50% 50%.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: