Cross-Browser für CSS-Rotation mit jquery.animate ()

80

Ich arbeite an der Erstellung einer browserübergreifenden Rotation (dh 9 +) und habe den folgenden Code in einer 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); 
}

Das CSS und HTML sind wirklich einfach und nur für die Demo:

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

Die Drehung funktioniert bei Verwendung, .css()jedoch nicht bei Verwendung .animate(). Warum ist das so und gibt es eine Möglichkeit, es zu beheben?

Vielen Dank.

frenchie
quelle
jQuery hat keine Ahnung, wie die Rotation animiert werden soll. Vielleicht CSS3-Übergänge verwenden?
John Dvorak
1
@ JanDvorak - außer dass IE9 keine CSS3-Übergänge unterstützt.
Spudley
1
Ich werde für den Teil "Fix it" stimmen (Sie könnten am Ende einen stepRückruf implementieren ), aber der Teil "Warum ist das so ?" Ist ziemlich klar.
John Dvorak
@Spudley: Ja, ich weiß: Das Ziel für die IE9-Unterstützung besteht darin, setInterval zu verwenden und die DoRotate-Funktion mehrmals aufzurufen.
Frenchie
Übrigens - Ich habe bereits in meiner Antwort auf Ihre andere Frage, die eine Polyfüllung für CSS-Übergänge im IE ist, auf die CSS-Sandpapierbibliothek hingewiesen. Vielleicht möchten Sie es versuchen.
Spudley

Antworten:

219

CSS-Transformationen können mit jQuery noch nicht animiert werden. Sie können so etwas tun:

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

Weitere Informationen zum Step-Callback finden Sie hier: http://api.jquery.com/animate/#step

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

Übrigens: Sie müssen css3-Transformationen nicht mit jQuery 1.7+ voranstellen

Aktualisieren

Sie können dies in ein jQuery-Plugin einbinden, um Ihr Leben ein bisschen einfacher zu machen:

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

Ich optimiert es ein wenig die Reihenfolge zu machen easing, durationund completeunbedeutend.

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

Update 2.1

Vielen Dank an matteo, der ein Problem mit dem this-context in the complete- festgestellt hat callback. Wenn dies behoben wurde, binden Sie den Rückruf mit jQuery.proxyauf jedem Knoten.

Ich habe die Edition bereits ab Update 2 zum Code hinzugefügt .

Update 2.2

Dies ist eine mögliche Änderung, wenn Sie beispielsweise die Drehung hin und her schalten möchten. Ich habe der Funktion einfach einen Startparameter hinzugefügt und diese Zeile ersetzt:

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

Wenn jemand weiß, wie er dies für alle Anwendungsfälle allgemeiner gestalten kann, unabhängig davon, ob er einen Startgrad festlegen möchte oder nicht, nehmen Sie bitte die entsprechende Änderung vor.


Die Verwendung ... ist ganz einfach!

Hauptsächlich haben Sie zwei Möglichkeiten, um das gewünschte Ergebnis zu erzielen. Aber zuerst werfen wir einen Blick auf die Argumente:

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

Mit Ausnahme von "Winkel" sind alle optional und greifen auf die Standardeigenschaften jQuery.fn.animatezurück:

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

1

Dieser Weg ist kurz, sieht aber etwas unklar aus, je mehr Argumente wir übergeben.

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

2 ..

Ich bevorzuge die Verwendung von Objekten, wenn mehr als drei Argumente vorhanden sind. Daher ist diese Syntax mein Favorit:

$(node).animateRotate(90, {
  duration: 1337,
  easing: 'linear',
  complete: function () {},
  step: function () {}
});
yckart
quelle
4
Können Sie das in eine Geige stecken?
Frenchie
4
Ok, sehr cool: das ist DAS Plugin für die browserübergreifende (IE9 +) CSS3-Rotation !! Sie können das behaupten: Sie haben das gebaut. Gute Arbeit!
Frenchie
1
@matteo Entschuldigung für die späte Antwort und vielen Dank für Ihren Test. Ich brauchte ein wenig Zeit, um das Problem zu lösen, aber ich habe es verstanden! fiddle.jshell.net/P5J4V/43 Übrigens habe ich Ihre Untersuchung in meiner Antwort erwähnt :)
yckart
1
@matteo Der Grund, warum thisnicht auf ein DOM-Objekt verwiesen wird, ist, dass der Kontext auf das Objekt festgelegt animate()wurde, auf das aufgerufen wurde, in diesem Fall {deg: 0}auf den Kontext. Sie können dies beheben, indem Sie den Kontext jeder Rückruffunktion mit apply()/ call()oder $.proxy()ändern (wie @yckart gezeigt hat). Hier ist meine Lösung, um ALLE Rückrufe zu beheben und 3D-Rotation zuzulassen: jsfiddle.net/TrevinAvery/P5J4V/44
Trevin Avery
1
Wenn Sie dasselbe Element immer wieder animieren möchten, führt das Starten 0jedes Mal mit Grad nicht zum erwarteten Verhalten. Sie müssen daher mit dem aktuellen Rotationswert initialisieren. Wie das geht, wird hier erklärt: stackoverflow.com/a/11840120/61818
Asbjørn Ulsberg
17

Danke yckart! Großartiger Beitrag. Ich habe dein Plugin ein bisschen weiter ausgearbeitet. StartAngle für volle Kontrolle und browserübergreifendes CSS hinzugefügt.

$.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
        });
    });
};
trüber Name
quelle
5
jQuery fügt das erforderliche Herstellerpräfix automatisch hinzu, sodass dies nicht erforderlich ist!
Yckart
+1 für die Querplattform. Toll. @yckart: Das automatische Präfix funktioniert in diesem Fall bei mir nicht.
lsmpascal
@PaxMaximinus Welche jQuery-Version verwenden Sie? blog.jquery.com/2012/08/09/jquery-1-8-released
yckart
@yckart: die 1.7.1 Version.
lsmpascal
1
@PaxMaximinus Wie Sie im Artikel von jquery-blog sehen können, ist das automatische Präfix erst seit jquery-1.8+!
Yckart
10

Der jQuery-Transit wird Ihnen wahrscheinlich das Leben erleichtern, wenn Sie mit CSS3-Animationen über jQuery arbeiten.

BEARBEITEN März 2014 (weil mein Rat seit meiner Veröffentlichung ständig auf und ab gewählt wurde)

Lassen Sie mich erklären, warum ich ursprünglich auf das obige Plugin hingewiesen habe:

Das Aktualisieren DOMbei jedem Schritt (dh $.animate) ist in Bezug auf die Leistung nicht ideal. Es funktioniert, ist aber höchstwahrscheinlich langsamer als reine CSS3-Übergänge oder CSS3-Animationen .

Dies liegt hauptsächlich daran, dass der Browser die Möglichkeit hat, vorauszudenken, wenn Sie angeben, wie der Übergang von Anfang bis Ende aussehen wird.

Dazu können Sie beispielsweise für jeden Status des Übergangs eine CSS-Klasse erstellen und den Animationsstatus nur mit jQuery umschalten.

Dies ist im Allgemeinen recht ordentlich, da Sie Ihre Animationen neben dem Rest Ihres CSS optimieren können, anstatt sie mit Ihrer Geschäftslogik zu verwechseln:

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

Wenn einer der Transformationsparameter dynamisch ist, können Sie stattdessen stattdessen das style-Attribut verwenden:

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

Viel detailliertere Informationen zu CSS3-Übergängen in MDN .

JEDOCH Es gibt ein paar andere Dinge im Auge zu behalten und das alles ein bisschen schwierig zu bekommen , wenn Sie komplexe Animationen haben, Verkettungs usw. und jQuery Transit nur macht alle kniffligen Bits unter der Haube:

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

Um diesen Cross-Browser einschließlich IE7 + auszuführen, müssen Sie das Plugin mit einer Transformationsmatrix erweitern. Da das Herstellerpräfix in jQuery ab jquery-1.8 + verwendet wird, werde ich dies für die transformEigenschaft weglassen .

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

Hinweis: Die Parameter optionsund startAnglesind optional, wenn Sie nur startAngleuse {}oder nullfor einstellen müssen options.

Anwendungsbeispiel:

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

Siehe auch diese jsfiddle für eine Demo.

Update : Sie können jetzt auch extra: {}die Optionen übergeben. Auf diese Weise können Sie andere Animationen gleichzeitig ausführen. Beispielsweise:

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

Dadurch wird das Element um 90 Grad gedreht, mit 100 Pixel nach rechts verschoben und während der Animation gleichzeitig halbtransparent.

Yeti
quelle
Oder IE9 in Firefox, aber nur Firefox.
Liam
Okay, es funktioniert jetzt in Chrome, Firefox und IE10. Kannst du IE9 testen, Liam? Das Problem war, dass die Transformationseigenschaft für Chrome und IE nicht definiert war. Daher war das Skript der Ansicht, dass die Transformationseigenschaft nicht verfügbar war. Daher änderte ich das Skript alle Präfixe sind: ms, o, webkit, mozErkennung korrekt zu gewährleisten. Die Geige wird ebenfalls auf v12 aktualisiert.
Yeti
2

Das ist meine Lösung:

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

dann können Sie es in der Standardanimation fkt verwenden:

//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' });
AntiCampeR
quelle
1

Eine andere Antwort, da jQuery.transit nicht mit jQuery.easing kompatibel ist. Diese Lösung wird als jQuery-Erweiterung geliefert. Ist allgemeiner, Rotation ist ein spezieller Fall:

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

Die Verwendung ist so einfach wie:

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

Ohne Plugin Cross Browser mit 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);
Alexey Alexeenka
quelle