CSS вращение кросс-браузер с jquery.animate()
Я работаю над созданием кросс-браузера совместимого вращения (ie9 +), и у меня есть следующий код в 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()
; почему это так и есть способ исправить это?
Спасибо.
Ответы
Ответ 1
CSS-Transforms невозможно анимировать с помощью 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 +
Update
Вы можете обернуть это в плагин 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
Update2
Я немного оптимизировал его, чтобы сделать порядок 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
-context в full- callback
. Если он исправлен, связываясь с обратным вызовом с jQuery.proxy
на каждом node.
Я добавил версию в код до Обновить 2.
Использование ... довольно просто!
В основном у вас есть два способа достичь желаемого результата. Но при первом взгляните на аргументы:
jQuery.fn.animateRotate(angle, duration, easing, complete)
За исключением "угла", все они являются необязательными и возвращаются к умолчанию jQuery.fn.animate
-properties:
duration: 400
easing: "swing"
complete: function () {}
первый
Этот путь является коротким, но выглядит немного неясным, чем больше аргументов мы передаем.
$(node).animateRotate(90);
$(node).animateRotate(90, function () {});
$(node).animateRotate(90, 1337, 'linear', function () {});
второй
Я предпочитаю использовать объекты, если есть более трех аргументов, поэтому этот синтаксис - это мой favorit:
$(node).animateRotate(90, {
duration: 1337,
easing: 'linear',
complete: function () {},
step: function () {}
});
Ответ 2
Спасибо 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
});
});
};
Ответ 3
jQuery transit, вероятно, облегчит вам жизнь, если вы будете заниматься анимацией CSS3 через jQuery.
EDIT March 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 ?
Ответ 4
Чтобы сделать этот кросс-браузер, включая 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
являются необязательными, если вам нужно только установить startAngle
использовать {}
или null
для 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 пикселей и сделает его полупрозрачным одновременно в течение анимации.
Ответ 5
это мое решение:
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' });
Ответ 6
Другой ответ, поскольку 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});
Ответ 7
Без перекрестного браузера с настройкой 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);