CSS rotacja w przeglądarce krzyżowej z jquery.animate ()

81

Pracuję nad utworzeniem rotacji kompatybilnej z różnymi przeglądarkami (ie9 +) i mam następujący kod w pliku 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); 
}

CSS i HTML są naprawdę proste i tylko do celów demonstracyjnych:

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

Rotacja działa podczas używania, .css()ale nie podczas używania .animate(); dlaczego tak jest i czy istnieje sposób, aby to naprawić?

Dzięki.

Francuz
źródło
jQuery nie ma pojęcia, jak animować rotację. Może wykorzystasz przejścia CSS3?
John Dvorak
1
@JanDvorak - poza tym, że IE9 nie obsługuje przejść CSS3.
Spudley
1
Głosuję za częścią „napraw to” (możesz w końcu zaimplementować stepwywołanie zwrotne), ale część „dlaczego tak jest” jest całkiem jasna.
John Dvorak
@Spudley: tak, wiem: celem wsparcia IE9 będzie użycie setInterval i kilkukrotne wywołanie funkcji DoRotate.
Frenchie
BTW - już wskazałem bibliotekę CSS Sandpaper w mojej odpowiedzi na Twoje drugie pytanie, która jest wypełnieniem dla CSS Transitions w IE. Możesz spróbować.
Spudley

Odpowiedzi:

222

Transformacje CSS nie są jeszcze możliwe do animacji za pomocą jQuery. Możesz zrobić coś takiego:

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

Możesz przeczytać więcej o step-callback tutaj: http://api.jquery.com/animate/#step

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

Przy okazji: nie musisz poprzedzać transformacji css3 jQuery 1.7+

Aktualizacja

Możesz owinąć to w wtyczkę jQuery, aby trochę ułatwić sobie życie:

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

Aktualizacja2

I zoptymalizowany go trochę, aby kolejność easing, durationi completenieistotne.

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

Aktualizacja 2.1.0

Podziękowania dla Matteo, który zauważył problem z this-context in the complete- callback. Jeśli naprawiono to przez powiązanie wywołania zwrotnego z jQuery.proxykażdym węzłem.

Dodałem edycję do kodu wcześniej z Update 2 .

Aktualizacja 2.2.0

Jest to możliwa modyfikacja, jeśli chcesz wykonać coś takiego, jak przełączanie rotacji w przód iw tył. Po prostu dodałem parametr start do funkcji i zastąpiłem tę linię:

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

Jeśli ktoś wie, jak uczynić to bardziej ogólnym dla wszystkich przypadków użycia, niezależnie od tego, czy chce ustawić stopień początkowy, proszę dokonać odpowiedniej edycji.


Użycie ... jest dość proste!

Zwykle masz dwa sposoby na osiągnięcie pożądanego rezultatu. Najpierw jednak spójrzmy na argumenty:

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

Z wyjątkiem „angle”, wszystkie są opcjonalne i jQuery.fn.animatezastępują domyślne właściwości:

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

1

Ten sposób jest krótki, ale wydaje się nieco niejasny, im więcej argumentów przekazujemy.

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

2nd

Wolę używać obiektów, jeśli jest więcej niż trzy argumenty, więc ta składnia jest moją ulubioną:

$(node).animateRotate(90, {
  duration: 1337,
  easing: 'linear',
  complete: function () {},
  step: function () {}
});
yckart
źródło
4
Czy możesz to włożyć w skrzypce?
Frenchie
4
Ok, bardzo fajnie: to jest wtyczka do rotacji CSS3 w różnych przeglądarkach (IE9 +) !! Możesz twierdzić, że: ty to zbudowałeś. Dobra robota!
Frenchie
1
@matteo Przepraszamy za spóźnioną odpowiedź i dziękuję za test. Potrzebowałem trochę czasu na rozwikłanie problemu, ale udało mi się! fiddle.jshell.net/P5J4V/43 Nawiasem mówiąc, wspomniałem o twoim dochodzeniu w mojej odpowiedzi :)
yckart
1
@matteo Przyczyną thisbraku odwołania do obiektu DOM jest to, że kontekst jest ustawiony na obiekt, który animate()został wywołany, w tym przypadku {deg: 0}jest ustawiony na kontekst. Możesz to naprawić, zmieniając kontekst każdej funkcji zwrotnej za pomocą apply()/ call()lub $.proxy()(jak pokazał @yckart). Oto moje rozwiązanie, aby naprawić WSZYSTKIE wywołania zwrotne i zezwolić na rotację 3D: jsfiddle.net/TrevinAvery/P5J4V/44
Trevin Avery
1
Jeśli chcesz animować ten sam element w kółko, rozpoczynanie od 0stopni za każdym razem nie doprowadzi do oczekiwanego zachowania, więc musisz zainicjować z bieżącą wartością obrotu. Jak to zrobić, wyjaśniono tutaj: stackoverflow.com/a/11840120/61818
Asbjørn Ulsberg
17

Dzięki yckart! Wielki wkład. Rozwinąłem nieco twoją wtyczkę. Dodano startAngle dla pełnej kontroli i CSS w różnych przeglądarkach.

$.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
        });
    });
};
drabname
źródło
5
jQuery automatycznie dodaje wymagany prefiks dostawcy, więc nie ma takiej potrzeby!
yckart
+1 za platformę krzyżową. Świetny. @yckart: w tym przypadku prefiks auto nie działa.
lsmpascal
@PaxMaximinus Jakiej wersji jQuery używasz? blog.jquery.com/2012/08/09/jquery-1-8-released
yckart
@yckart: wersja 1.7.1.
lsmpascal
1
@PaxMaximinus Jak widać w artykule z jquery-blog, automatyczne prefiksowanie trwa właśnie od tego czasu jquery-1.8+!
yckart
10

Tranzyt jQuery prawdopodobnie ułatwi Ci życie, jeśli masz do czynienia z animacjami CSS3 za pośrednictwem jQuery.

EDYTUJ marzec 2014 (ponieważ moja rada była stale głosowana w górę iw dół, odkąd ją opublikowałem)

Pozwól, że wyjaśnię, dlaczego początkowo sugerowałem powyżej wtyczkę:

Aktualizacja DOMna każdym kroku (tj. $.animate) Nie jest idealna pod względem wydajności. Działa, ale najprawdopodobniej będzie wolniejsze niż czyste przejścia CSS3 lub animacje CSS3 .

Dzieje się tak głównie dlatego, że przeglądarka ma szansę przemyśleć przyszłość, jeśli wskażesz, jak będzie wyglądać przejście od początku do końca.

Aby to zrobić, możesz na przykład utworzyć klasę CSS dla każdego stanu przejścia i używać jQuery tylko do przełączania stanu animacji.

Zwykle jest to całkiem fajne, ponieważ możesz modyfikować animacje wraz z resztą swojego CSS, zamiast mieszać je z logiką biznesową:

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

Jeśli którykolwiek z parametrów transformacji jest dynamiczny, możesz oczywiście zamiast tego użyć atrybutu style:

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

Bardziej szczegółowe informacje o przejściach CSS3 na MDN .

JEDNAK Jest kilka innych rzeczy, o których należy pamiętać, a wszystko to może być nieco skomplikowane, jeśli masz złożone animacje, łańcuchy itp., A jQuery Transit po prostu wykonuje wszystkie trudne elementy pod maską:

$('.eye').transit({ rotate: '90deg'}); // easy huh ?
Theo.T
źródło
3

Aby wykonać tę przeglądarkę, w tym IE7 +, musisz rozszerzyć wtyczkę o macierz transformacji. Ponieważ prefiks dostawcy jest wykonywany w jQuery od jquery-1.8 +, zostawię to dla transformwłaściwości.

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

Uwaga: parametry optionsi startAnglesą opcjonalne, jeśli potrzebujesz tylko ustawić startAngleużycie {}lub nulldla options.

Przykładowe użycie:

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

Zobacz także to jsfiddle, aby zobaczyć demo.

Aktualizacja : Możesz teraz również przekazać extra: {}opcje. Umożliwi to jednoczesne wykonywanie innych animacji. Na przykład:

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

Spowoduje to obrócenie elementu o 90 stopni i przesunięcie go w prawo o 100 pikseli i uczyni go półprzezroczystym w tym samym czasie podczas animacji.

Yeti
źródło
Lub IE9, działa w Firefoksie, ale tylko w Firefoksie.
Liam
Dobra, więc działa teraz w Chrome, Firefox i IE10. Czy możesz przetestować IE9, Liam? Problem polegał na tym, że właściwość transform była niezdefiniowana dla Chrome i IE, dlatego skrypt uznał, że właściwość transform jest niedostępna. Stąd, zmieniłem skrypt obejmować wszystkie przedrostki: ms, o, webkit, moz, aby zapewnić wykrywanie prawidłowo. Skrzypce również zostały zaktualizowane do wersji 12.
Yeti
2

to jest moje rozwiązanie:

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

możesz go użyć w domyślnym animowanym pliku fkt:

//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
źródło
1

Inna odpowiedź, ponieważ jQuery.transit nie jest kompatybilny z jQuery.easing. To rozwiązanie jest dostępne jako rozszerzenie jQuery. Jest bardziej ogólny, rotacja to szczególny przypadek:

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

Użycie jest tak proste, jak:

$(element).animateStep({from: 0, to: 90, step: $.fn.rotate});
Opony
źródło
0

Bez wtyczek i przeglądarki z 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
źródło