Поперечний браузер обертання CSS з jquery.animate ()


82

Я працюю над створенням крос-браузерної сумісної ротації (тобто 9 +), і у мене є такий код у 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 і HTML дійсно прості і просто для демонстрації:

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

Обертання працює під час використання, .css()але не під час використання .animate(); чому це так і чи є спосіб це виправити?

Дякую.


jQuery не уявляє, як анімувати обертання. Можливо, використовувати переходи CSS3?
Джон Дворжак

1
@JanDvorak - за винятком того, що IE9 не підтримує переходи CSS3.
Spudley 03.03.13

1
Я підтримую частину "виправити" (можливо, ви в результаті реалізуєте stepзворотний виклик), але частина "чому це так" досить зрозуміла.
Джон Дворжак

@Spudley: так, я знаю: метою підтримки IE9 буде використання setInterval і виклик функції DoRotate кілька разів.
французька 03.03.13

До речі - я вже вказував на бібліотеку наждачного паперу CSS у своїй відповіді на інше ваше питання, яке є полізаповненням для переходів CSS в IE. Можливо, ви захочете спробувати.
Spudley 03.03.13

Відповіді:


223

CSS-перетворення поки що неможливо анімувати за допомогою jQuery. Ви можете зробити щось подібне:

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

Детальніше про зворотний виклик можна прочитати тут: http://api.jquery.com/animate/#step

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

І, до речі: вам не потрібно префіксувати перетворення css3 за допомогою jQuery 1.7+

Оновлення

Ви можете обернути це у jQuery-плагін, щоб полегшити своє життя:

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

Оновлення2

Я трохи оптимізував його, щоб зробити порядок easing, durationі completeнезначний.

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

Оновлення 2.1

Дякую Matteo, який зазначив проблему з this-контекстом у повному обсязі- callback. Якщо це виправлено, прив’язавши зворотний виклик до jQuery.proxyкожного вузла.

Я додав видання в код раніше з оновлення 2 .

Оновлення 2.2

Це можлива модифікація, якщо ви хочете зробити щось на зразок перемикання обертання вперед-назад. Я просто додав параметр старту до функції і замінив цей рядок:

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

Якщо хтось знає, як зробити це загальнішим для всіх випадків використання, незалежно від того, хоче він встановити початковий ступінь, будь ласка, внесіть відповідне редагування.


Використання ... досить просте!

В основному у вас є два шляхи досягнення бажаного результату. Але спочатку давайте розглянемо аргументи:

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

За винятком "кут", всі вони є необов'язковими і повертаються до властивостей за замовчуванням jQuery.fn.animate:

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

1-й

Цей шлях є коротким, але виглядає дещо незрозумілим, чим більше аргументів ми передаємо.

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

2-й

Я вважаю за краще використовувати об'єкти, якщо аргументів більше трьох, тому цей синтаксис є моїм фаворитом:

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

4
Чи можете ви помістити це в скрипку?
французька 03.03.13

4
Гаразд, дуже класно: це Плагін для крос-браузерного (IE9 +) обертання CSS3 !! Ви можете стверджувати, що: ви це збудували. Хороша робота!
французька 04.03.13

1
@matteo Вибачте за пізню відповідь та дякую за ваш тест. Мені потрібно було трохи часу, щоб вирішити проблему, але я зрозумів! fiddle.jshell.net/P5J4V/43 До речі, я згадав про ваше розслідування у своєму
анвері

1
@matteo Причина thisне посилається на об'єкт DOM, оскільки контекст встановлений для того, щоб об'єкт animate()був викликаний, у цьому випадку {deg: 0}встановлений контекст. Ви можете це виправити, змінивши контекст кожної функції зворотного виклику за допомогою apply()/ call()або $.proxy()(як показав @yckart). Ось моє рішення для виправлення ВСІХ зворотних викликів і дозволу обертання 3D: jsfiddle.net/TrevinAvery/P5J4V/44
Тревін Евері

1
Якщо ви хочете анімувати один і той же елемент знову і знову, 0щоразу , починаючи з градусів, це не призведе до очікуваної поведінки, тому вам потрібно ініціалізувати поточне значення обертання. Як це зробити, описано тут: stackoverflow.com/a/11840120/61818
Асбьyoрн Ulsberg

17

Дякую yckart! Великий внесок. Я трохи більше довідався про ваш плагін. Додано startAngle для повного контролю та css-переглядача.

$.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
jQuery автоматично додає необхідний префікс постачальника, тому в цьому немає потреби!
yckart

+1 для крос-платформи. Чудово. @yckart: у цьому випадку автоматичний префікс не працює для мене.
lsmpascal

@PaxMaximinus Яку версію jQuery ви використовуєте? blog.jquery.com/2012/08/09/jquery-1-8-випущено
yckart

@yckart: версія 1.7.1.
lsmpascal

1
@PaxMaximinus Як ви можете бачити в статті з jquery-blog, автоматичне додавання префіксів саме з тих пір jquery-1.8+!
yckart

10

Перенесення jQuery , мабуть, полегшить вам життя, якщо ви маєте справу з анімацією CSS3 через jQuery.

РЕДАКТУВАТИ березень 2014 р. (Оскільки за мою пораду постійно голосували “вгору і вниз”)

Дозвольте мені пояснити, чому я спочатку натякав на плагін вище:

Оновлення DOMна кожному кроці (тобто $.animate) не є ідеальним з точки зору продуктивності. Це працює, але, швидше за все, буде повільнішим, ніж чисті переходи CSS3 або анімації CSS3 .

Це головним чином тому, що браузер отримує можливість думати заздалегідь, якщо ви вкажете, як буде виглядати перехід від початку до кінця.

Для цього ви можете, наприклад, створити клас CSS для кожного стану переходу і використовувати jQuery лише для перемикання стану анімації.

Це, як правило, досить акуратно, оскільки ви можете налаштувати анімацію разом із рештою вашого CSS, замість того, щоб змішувати це з вашою бізнес-логікою:

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

Якщо будь-який із параметрів перетворення є динамічним, ви можете, звичайно, використовувати атрибут 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)'
    }); 
});

Набагато детальніша інформація про переходи CSS3 на MDN .

Втім, є кілька інших речей, про які слід пам’ятати, і все це може стати дещо складним, якщо у вас є складні анімації, ланцюжки тощо, а jQuery Transit просто робить усі хитрі біти під капотом:

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

3

Для цього крос-браузера, включаючи IE7 +, потрібно розширити плагін матрицею перетворень. Оскільки префікс постачальника робиться в jQuery з jquery-1.8 +, я залишу це для 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);
        }
    });
};

Примітка: Параметри optionsта startAngleє необов’язковими, якщо вам потрібно лише встановити startAngleuse {}або nullfor options.

Приклад використання:

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

Див. Також цей jsfiddle для демонстрації.

Оновлення : Тепер ви також можете передати extra: {}параметри. Це дозволить вам одночасно виконувати інші анімації. Наприклад:

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

Це поверне елемент на 90 градусів, перемістить його вправо на 100 пікселів і зробить його напівпрозорим одночасно під час анімації.


Або IE9, робить це у Firefox, але тільки firefox.
Ліам

Гаразд, це працює зараз у Chrome, Firefox та IE10. Чи можете ви протестувати IE9, Ліаме? Проблема полягала в тому, що властивість transform не було визначено для Chrome та IE, тому сценарій вважав, що властивість transform недоступна. Таким чином, я змінив сценарій , щоб включити всі префікси: ms, o, webkit, mozщоб правильно забезпечити виявлення. Скрипка також оновлена ​​до v12.
Єті

2

це моє рішення:

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

тоді ви можете використовувати його в типовому анімаційному 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' });

1

Інша відповідь, оскільки jQuery.transit не сумісний з jQuery.easing. Це рішення поставляється як розширення jQuery. Більш загальний, ротація - це конкретний випадок:

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

Використання таке просте, як:

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

0

Без перехресного браузера плагіна з 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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.