Navigateur croisé CSS rotation avec jquery.animer()

je travaille sur la création d'une rotation compatible cross-browser (ie9+) et j'ai le code suivant dans un 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); 
}

le CSS et HTML sont vraiment simples et juste pour la démo:

.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 rotation fonctionne quand on utilise .css() mais pas quand on utilise .animate() ; pourquoi cela et y a-t-il un moyen de le corriger?

Merci.

75
demandé sur frenchie 2013-03-04 01:22:44

7 réponses

css-les transformations ne sont pas encore possibles à animer avec jQuery. Vous pouvez faire quelque chose comme ceci:

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)'
            });
        }
    });
}

vous pouvez en savoir plus sur le step-callback ici: http://api.jquery.com/animate/#step

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

et, btw: vous n'avez pas besoin de préfixer les transformations css3 avec jQuery 1.7+

mise à Jour

vous pouvez envelopper cela dans un jQuery-plugin pour rendre votre vie un peu plus facile:

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

Update2

Je l'ai optimisé un peu pour rendre l'ordre de easing , duration et complete insignifiant.

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

mise à jour 2.1

merci à matteo qui a noté un problème avec le this -contexte dans le complet- callback . Si fixé par liant le rappel avec jQuery.proxy sur chaque noeud.

j'ai ajouté l'édition au code avant de mise à jour 2 .

mise à Jour 2.2

c'est une modification possible si vous voulez faire quelque chose comme basculer la rotation d'avant en arrière. J'ai simplement ajouté un paramètre de démarrage de la

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

si quelqu'un sait comment rendre ce plus générique pour tous les cas d'utilisation, qu'il veuille ou non définir un degré de départ, s'il vous plaît faire l'édition appropriée.


L'Usage ...est assez simple!

vous avez principalement deux façons d'atteindre le résultat souhaité. Mais d'abord, regardons les arguments:

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

sauf "angle" sont tous optionnels et retombent à la valeur par défaut jQuery.fn.animate - propriétés:

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

1er

de cette façon est le court, mais semble un peu flou les arguments plus que nous passons.

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

2ème

je préfère utiliser des objets s'il y a plus de trois arguments, donc cette syntaxe est ma préférée:

$(node).animateRotate(90, {
  duration: 1337,
  easing: 'linear',
  complete: function () {},
  step: function () {}
});
204
répondu yckart 2018-04-25 22:42:46

Merci yckart! Un grand apport. J'ai étoffé votre plugin un peu plus. Ajout de startAngle pour le contrôle complet et le CSS de cross-browser.

$.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
répondu drabname 2013-07-10 19:23:44

jQuery transit vous facilitera probablement la vie si vous avez affaire à des animations CSS3 à travers jQuery.

modifier mars 2014 (parce que mon conseil a constamment été voté de haut en bas depuis que je l'ai publié)

permettez-moi d'expliquer pourquoi je faisais allusion initialement vers le plugin ci-dessus:

mise à jour du DOM sur chaque marche (i.e. $.animate ) n'est pas idéal en termes de performance. Il fonctionne, mais sera probablement plus lent que pure transitions CSS3 ou les animations CSS3 .

c'est principalement parce que le navigateur a une chance de penser à l'avance si vous indiquez à quoi ressemblera la transition du début à la fin.

Pour ce faire, vous pouvez par exemple créer une classe CSS pour chaque état de la transition et n'utilisez jQuery que pour basculer l'état d'animation.

c'est généralement très soigné car vous pouvez ajuster vos animations avec le reste de votre CSS au lieu de les confondre avec votre logique d'entreprise:

// initial state
.eye {
   -webkit-transform: rotate(45deg);
   -moz-transform: rotate(45deg);
   transform: rotate(45deg);
   // etc.

   // transition settings
   -webkit-transition: -webkit-transform 1s linear 0.2s;
   -moz-transition: -moz-transform 1s linear 0.2s;
   transition: transform 1s linear 0.2s;
   // etc.
}

// open state    
.eye.open {

   transform: rotate(90deg);
}

// Javascript
$('.eye').on('click', function () { $(this).addClass('open'); });

Si l'un des paramètres de transformation est dynamique, vous pouvez bien sûr utiliser l'attribut style plutôt:

$('.eye').on('click', function () { 
    $(this).css({ 
        -webkit-transition: '-webkit-transform 1s ease-in',
        -moz-transition: '-moz-transform 1s ease-in',
        // ...

        // note that jQuery will vendor prefix the transform property automatically
        transform: 'rotate(' + (Math.random()*45+45).toFixed(3) + 'deg)'
    }); 
});

beaucoup plus d'informations détaillées sur CSS3 transitions sur MDN .

TOUTEFOIS Il ya quelques autres choses à garder à l'esprit et tout cela peut être un peu difficile si vous avez des animations complexes, enchaînant etc. et jQuery Transit ne vient tous les passages techniques sous le capot:

$('.eye').transit({ rotate: '90deg'}); // easy huh ?
10
répondu Theo.T 2014-03-26 13:44:18

pour faire ce navigateur croisé y compris IE7+, vous aurez besoin d'étendre le plugin avec une matrice de transformation. Puisque le préfixe vendeur est fait en jQuery à partir de jquery-1.8+ je vais laisser cela pour la propriété transform .

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

Note: les paramètres options et startAngle sont facultatifs, s'il vous suffit de définir startAngle utilisez {} ou null pour options .

exemple d'usage:

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

Voir Aussi ce jsfiddle pour une démo.

Update : vous pouvez maintenant aussi passer extra: {} dans les options. Cela vous permettra d'exécuter d'autres animations simultanément. Par exemple:

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

cela fera pivoter l'élément de 90 degrés, le déplacera vers la droite avec 100px et le rendra semi-transparent en même temps pendant l'animation.

3
répondu Yeti 2014-02-10 11:23:06

c'est ma solution:

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

alors vous pouvez l'utiliser dans l'animate FKT par défaut:

//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
répondu AntiCampeR 2015-05-09 18:39:16

une autre réponse, parce que jQuery.le transit n'est pas compatible avec jQuery.soulager. Cette solution est une extension jQuery. Est plus générique, la rotation est un cas spécifique:

$.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)");
    }
});

l'usage est aussi simple que:

$(element).animateStep({from: 0, to: 90, step: $.fn.rotate});
1
répondu Tires 2017-10-17 14:25:28

sans plugin cross browser avec 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
répondu Alexey Alexeenka 2016-03-15 10:13:08