88 votes

Rotation CSS entre navigateurs avec jquery.animate()

Je travaille à la création d'une rotation compatible avec tous les navigateurs (ie9+) et j'ai le code suivant dans un fichier de type 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 le HTML sont vraiment simples et juste pour la démonstration :

.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 lorsque l'on utilise .css() mais pas en utilisant .animate() Pourquoi ? Y a-t-il un moyen de résoudre ce problème ?

Merci.

0 votes

JQuery n'a aucune idée de la façon d'animer la rotation. Peut-être faut-il utiliser les transitions CSS3 ?

1 votes

@JanDvorak - sauf que IE9 ne supporte pas les transitions CSS3.

1 votes

Je vais donner un vote positif pour la partie "réparer" (vous pourriez finir par mettre en œuvre un système de gestion de l'information). step ), mais la partie "pourquoi ça" est assez claire.

239voto

yckart Points 7517

Les transformations CSS ne peuvent pas encore être animées 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 rappel par étapes ici : http://api.jquery.com/animate/#step

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

Et, au fait : il n'est pas nécessaire de préfixer les transformées css3 avec jQuery 1.7+.

Mise à jour

Vous pouvez l'intégrer dans un plugin jQuery pour vous faciliter la vie :

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

Mise à jour2

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

$.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 l'ensemble- callback . Si on l'a réparé en liaison le rappel avec jQuery.proxy sur chaque nœud.

J'ai déjà ajouté l'édition au code à partir de Mise à jour 2 .

Mise à jour 2.2

C'est une modification possible si vous voulez faire quelque chose comme basculer la rotation en avant et en arrière. J'ai simplement ajouté un paramètre de départ à la fonction et remplacé cette ligne :

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

Si quelqu'un sait comment rendre cela plus générique pour tous les cas d'utilisation, qu'il s'agisse ou non de définir un degré de départ, veuillez faire la modification appropriée.


L'utilisation ...est très simple !

Principalement, vous avez deux façons d'atteindre le résultat souhaité. Mais pour la première, jetons un coup d'oeil aux arguments :

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

A l'exception de "angle", elles sont toutes optionnelles et reviennent à la valeur par défaut. jQuery.fn.animate -propriétés :

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

1er

Cette méthode est la plus courte, mais elle n'est pas très claire si l'on passe plus d'arguments.

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

4 votes

Vous pouvez mettre ça dans un violon ?

0 votes

@frenchie Je suis sur iPhone, aucune chance depuis que jsFiddle a mis à jour leur éditeur ;) cependant, je vais vous préparer un bac...

0 votes

@frenchie J'ai oublié quelque chose ! J'ai mis à jour ma réponse maintenant... Voici un exemple fonctionnel : jsbin.com/ofagog/2/edit

20voto

drabname Points 91

Merci yckart ! Excellente contribution. J'ai étoffé votre plugin un peu plus. J'ai ajouté startAngle pour un contrôle total et des css multi-navigateurs.

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

5 votes

JQuery ajoute automatiquement le préfixe fournisseur nécessaire, ce qui est inutile !

0 votes

+1 pour la plateforme croisée. Super. @yckart : le préfixe automatique ne fonctionne pas pour moi dans ce cas.

0 votes

@PaxMaximinus Quelle version de jQuery utilisez-vous ? blog.jquery.com/2012/08/09/jquery-1-8-released

11voto

Theo.T Points 4739

Transit jQuery vous facilitera probablement la vie si vous avez affaire à des animations CSS3 via jQuery.

EDIT mars 2014 (parce que mes conseils ont été constamment votés et rejetés depuis que je les ai postés).

Permettez-moi d'expliquer pourquoi je faisais initialement allusion au plugin ci-dessus :

Mise à jour de la DOM à chaque étape (c'est-à-dire $.animate ) n'est pas idéal en termes de performances. Elle fonctionne, mais sera très probablement plus lente qu'un pur Transitions CSS3 o Animations CSS3 .

Cela est dû principalement au fait que le navigateur a la possibilité de réfléchir à 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 utiliser jQuery uniquement pour faire basculer l'état de l'animation.

C'est généralement très pratique, car vous pouvez modifier vos animations en même temps que le reste de votre CSS au lieu de les mélanger à votre logique commerciale :

// 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 à la place :

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

Des informations beaucoup plus détaillées sur Transitions CSS3 sur MDN .

CEPENDANT, Il y a quelques autres choses à garder à l'esprit et tout cela peut devenir un peu délicat si vous avez des animations complexes, des enchaînements etc. et jQuery Transit s'occupe juste de toutes les parties délicates sous le capot :

$('.eye').transit({ rotate: '90deg'}); // easy huh ?

4voto

Yeti Points 429

Pour faire cela dans tous les navigateurs, y compris IE7+, vous devrez développer le plugin avec une matrice de transformation. Puisque le préfixe du fournisseur est fait dans jQuery à partir de jquery-1.8+, je vais laisser cela de côté pour le transform propriété.

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

Remarque : Les paramètres options y startAngle sont facultatifs, si vous avez seulement besoin de définir startAngle utiliser {} o null pour options .

Exemple d'utilisation :

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 jsfiddle pour une démonstration.

Mise à jour : 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 de 100px et le rendra semi-transparent, tout cela en même temps pendant l'animation.

0 votes

Ou IE9, fait dans Firefox, mais seulement firefox.

0 votes

Bon, cela fonctionne maintenant dans Chrome, Firefox et IE10. Peux-tu tester IE9, Liam ? Le problème était que la propriété transform était indéfinie pour Chrome et IE, donc le script pensait que la propriété transform était indisponible. J'ai donc modifié le script pour inclure tous les préfixes : ms , o , webkit , moz pour assurer une détection correcte. Le fiddle est également mis à jour à la v12.

2voto

AntiCampeR Points 21

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X