Pregunta Navegador cruzado de rotación de CSS con jquery.animate ()


Estoy trabajando en la creación de una rotación compatible con varios navegadores (es decir, 9 +) y tengo el siguiente código en una jsfiddle

$(document).ready(function () { 
    DoRotate(30);
    AnimateRotate(30);
});

function DoRotate(d) {

    $("#MyDiv1").css({
          '-moz-transform':'rotate('+d+'deg)',
          '-webkit-transform':'rotate('+d+'deg)',
          '-o-transform':'rotate('+d+'deg)',
          '-ms-transform':'rotate('+d+'deg)',
          'transform': 'rotate('+d+'deg)'
     });  
}

function AnimateRotate(d) {

        $("#MyDiv2").animate({
          '-moz-transform':'rotate('+d+'deg)',
          '-webkit-transform':'rotate('+d+'deg)',
          '-o-transform':'rotate('+d+'deg)',
          '-ms-transform':'rotate('+d+'deg)',
          'transform':'rotate('+d+'deg)'
     }, 1000); 
}

El CSS y HTML son realmente simples y solo para demostración:

.SomeDiv{
    width:50px;
    height:50px;       
    margin:50px 50px;
    background-color: red;}

<div id="MyDiv1" class="SomeDiv">test</div>
<div id="MyDiv2" class="SomeDiv">test</div>

La rotación funciona cuando se usa .css() pero no cuando se usa .animate(); ¿Por qué es eso y hay una manera de solucionarlo?

Gracias.


74
2018-03-03 21:22


origen


Respuestas:


CSS-Transforms aún no es posible animar con jQuery. Puedes hacer algo como esto:

function AnimateRotate(angle) {
    // caching the object for performance reasons
    var $elem = $('#MyDiv2');

    // we use a pseudo object for the animation
    // (starts from `0` to `angle`), you can name it as you want
    $({deg: 0}).animate({deg: angle}, {
        duration: 2000,
        step: function(now) {
            // in the step-callback (that is fired each step of the animation),
            // you can use the `now` paramter which contains the current
            // animation-position (`0` up to `angle`)
            $elem.css({
                transform: 'rotate(' + now + 'deg)'
            });
        }
    });
}

Puede leer más sobre la devolución de llamada aquí: http://api.jquery.com/animate/#step

http://jsfiddle.net/UB2XR/23/

Y, por cierto, no es necesario prefijar las transformaciones css3 con jQuery 1.7+

Actualizar

Puede envolver esto en un jQuery-plugin para hacer su vida un poco más fácil:

$.fn.animateRotate = function(angle, duration, easing, complete) {
  return this.each(function() {
    var $elem = $(this);

    $({deg: 0}).animate({deg: angle}, {
      duration: duration,
      easing: easing,
      step: function(now) {
        $elem.css({
           transform: 'rotate(' + now + 'deg)'
         });
      },
      complete: complete || $.noop
    });
  });
};

$('#MyDiv2').animateRotate(90);

http://jsbin.com/ofagog/2/edit

Actualización2

Lo optimicé un poco para hacer el orden de easing, duration y complete insignificante.

$.fn.animateRotate = function(angle, duration, easing, complete) {
  var args = $.speed(duration, easing, complete);
  var step = args.step;
  return this.each(function(i, e) {
    args.complete = $.proxy(args.complete, e);
    args.step = function(now) {
      $.style(e, 'transform', 'rotate(' + now + 'deg)');
      if (step) return step.apply(e, arguments);
    };

    $({deg: 0}).animate({deg: angle}, args);
  });
};

Actualización 2.1

Gracias a matteo quien notó un problema con el this-contexto en el completo-callback. Si lo arreglaste por Unión la devolución de llamada con jQuery.proxy en cada nodo.

He agregado la edición al código antes de Actualización 2.

Actualización 2.2

Esta es una posible modificación si desea hacer algo como alternar la rotación hacia adelante y hacia atrás. Simplemente agregué un parámetro de inicio a la función y reemplacé esta línea:

$({deg: start}).animate({deg: angle}, args);

Si alguien sabe cómo hacer que esto sea más genérico para todos los casos de uso, independientemente de si desean o no establecer un grado de inicio, realice la edición adecuada.


El uso... es bastante simple!

Principalmente tienes dos formas de alcanzar el resultado deseado. Pero al principio, echemos un vistazo a los argumentos:

jQuery.fn.animateRotate(angle, duration, easing, complete)

Excepto por "ángulo" son todos ellos opcionales y están de regreso a los valores predeterminados jQuery.fn.animate-propiedades:

duration: 400
easing: "swing"
complete: function () {}

1ra

De esta manera es corto, pero parece un poco confuso cuanto más argumentos se transmiten.

$(node).animateRotate(90);
$(node).animateRotate(90, function () {});
$(node).animateRotate(90, 1337, 'linear', function () {});

2nd

Prefiero usar objetos si hay más de tres argumentos, por lo que esta sintaxis es mi favor:

$(node).animateRotate(90, {
  duration: 1337,
  easing: 'linear',
  complete: function () {},
  step: function () {}
});

201
2017-07-10 19:23



Gracias yckart! Gran contribución Completé tu plugin un poco más. Se ha agregado startAngle para un control total y CSS entre navegadores.

$.fn.animateRotate = function(startAngle, endAngle, duration, easing, complete){
    return this.each(function(){
        var elem = $(this);

        $({deg: startAngle}).animate({deg: endAngle}, {
            duration: duration,
            easing: easing,
            step: function(now){
                elem.css({
                  '-moz-transform':'rotate('+now+'deg)',
                  '-webkit-transform':'rotate('+now+'deg)',
                  '-o-transform':'rotate('+now+'deg)',
                  '-ms-transform':'rotate('+now+'deg)',
                  'transform':'rotate('+now+'deg)'
                });
            },
            complete: complete || $.noop
        });
    });
};

17
2018-03-03 21:38



jQuery tránsito probablemente le hará la vida más fácil si está tratando con animaciones CSS3 a través de jQuery.

EDITAR Marzo de 2014


10
2017-10-17 11:27



Para hacer este navegador cruzado que incluye IE7 +, deberá expandir el complemento con una matriz de transformación. Dado que el prefijo del proveedor está hecho en jQuery desde jquery-1.8 + lo dejaré para el transform propiedad.

$.fn.animateRotate = function(endAngle, options, startAngle)
{
    return this.each(function()
    {
        var elem = $(this), rad, costheta, sintheta, matrixValues, noTransform = !('transform' in this.style || 'webkitTransform' in this.style || 'msTransform' in this.style || 'mozTransform' in this.style || 'oTransform' in this.style),
            anims = {}, animsEnd = {};
        if(typeof options !== 'object')
        {
            options = {};
        }
        else if(typeof options.extra === 'object')
        {
            anims = options.extra;
            animsEnd = options.extra;
        }
        anims.deg = startAngle;
        animsEnd.deg = endAngle;
        options.step = function(now, fx)
        {
            if(fx.prop === 'deg')
            {
                if(noTransform)
                {
                    rad = now * (Math.PI * 2 / 360);
                    costheta = Math.cos(rad);
                    sintheta = Math.sin(rad);
                    matrixValues = 'M11=' + costheta + ', M12=-'+ sintheta +', M21='+ sintheta +', M22='+ costheta;
                    $('body').append('Test ' + matrixValues + '<br />');
                    elem.css({
                        'filter': 'progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\','+matrixValues+')',
                        '-ms-filter': 'progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\','+matrixValues+')'
                    });
                }
                else
                {
                    elem.css({
                        //webkitTransform: 'rotate('+now+'deg)',
                        //mozTransform: 'rotate('+now+'deg)',
                        //msTransform: 'rotate('+now+'deg)',
                        //oTransform: 'rotate('+now+'deg)',
                        transform: 'rotate('+now+'deg)'
                    });
                }
            }
        };
        if(startAngle)
        {
            $(anims).animate(animsEnd, options);
        }
        else
        {
            elem.animate(animsEnd, options);
        }
    });
};

Nota: los parámetros options y startAngle son opcionales, si solo necesitas configurar startAngle utilizar {} o null para options.

Ejemplo de uso:

var obj = $(document.createElement('div'));
obj.on("click", function(){
    obj.stop().animateRotate(180, {
        duration: 250,
        complete: function()
        {
            obj.animateRotate(0, {
                duration: 250
            });
        }
    });
});
obj.text('Click me!');
obj.css({cursor: 'pointer', position: 'absolute'});
$('body').append(obj);

Ver también esto jsfiddle para una demostración

Actualizar: Ahora también puede pasar extra: {} en las opciones. Esto te permitirá ejecutar otras animaciones simultáneamente. Por ejemplo:

obj.animateRotate(90, {extra: {marginLeft: '100px', opacity: 0.5}});

Esto rotará el elemento 90 grados, lo moverá hacia la derecha con 100 píxeles y lo hará semitransparente, todo al mismo tiempo durante la animación.


3
2018-05-09 17:54



esta es mi solución:

var matrixRegex = /(?:matrix\(|\s*,\s*)([-+]?[0-9]*\.?[0-9]+(?:[e][-+]?[0-9]+)?)/gi;

var getMatches = function(string, regex) {
    regex || (regex = matrixRegex);
    var matches = [];
    var match;
    while (match = regex.exec(string)) {
        matches.push(match[1]);
    }
    return matches;
};

$.cssHooks['rotation'] = {
    get: function(elem) {
        var $elem = $(elem);
        var matrix = getMatches($elem.css('transform'));
        if (matrix.length != 6) {
            return 0;
        }
        return Math.atan2(parseFloat(matrix[1]), parseFloat(matrix[0])) * (180/Math.PI);
    }, 
    set: function(elem, val){
        var $elem = $(elem);
        var deg = parseFloat(val);
        if (!isNaN(deg)) {
            $elem.css({ transform: 'rotate(' + deg + 'deg)' });
        }
    }
};
$.cssNumber.rotation = true;
$.fx.step.rotation = function(fx) {
    $.cssHooks.rotation.set(fx.elem, fx.now + fx.unit);
};

entonces puedes usarlo en el fkt animado predeterminado:

//rotate to 90 deg cw
$('selector').animate({ rotation: 90 });

//rotate to -90 deg ccw
$('selector').animate({ rotation: -90 });

//rotate 90 deg cw from current rotation
$('selector').animate({ rotation: '+=90' });

//rotate 90 deg ccw from current rotation
$('selector').animate({ rotation: '-=90' });

2
2018-03-22 18:57



Otra respuesta, porque jQuery.transit no es compatible con jQuery.easing. Esta solución viene como una extensión jQuery. Es más genérico, la rotación es un caso específico:

$.fn.extend({
    animateStep: function(options) {
        return this.each(function() {
            var elementOptions = $.extend({}, options, {step: options.step.bind($(this))});
            $({x: options.from}).animate({x: options.to}, elementOptions);
        });
    },
    rotate: function(value) {
        return this.css("transform", "rotate(" + value + "deg)");
    }
});

El uso es tan simple como:

$(element).animateStep({from: 0, to: 90, step: $.fn.rotate});

1
2018-03-15 10:13



Sin plugin cross browser con setInterval:

                        function rotatePic() {
                            jQuery({deg: 0}).animate(
                               {deg: 360},  
                               {duration: 3000, easing : 'linear', 
                                 step: function(now, fx){
                                   jQuery("#id").css({
                                      '-moz-transform':'rotate('+now+'deg)',
                                      '-webkit-transform':'rotate('+now+'deg)',
                                      '-o-transform':'rotate('+now+'deg)',
                                      '-ms-transform':'rotate('+now+'deg)',
                                      'transform':'rotate('+now+'deg)'
                                  });
                              }
                            });
                        }

                        var sec = 3;
                        rotatePic();
                        var timerInterval = setInterval(function() {
                            rotatePic();
                            sec+=3;
                            if (sec > 30) {
                                clearInterval(timerInterval);
                            }
                        }, 3000);

0