CSS вращение кросс-браузер с jquery.animate()
Я работаю над созданием кросс-браузерной совместимой ротации (т.е. 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()
; почему это так и есть ли способ это исправить?
Благодарю.
7 ответов
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/
И, кстати: вам не нужно префикс преобразует 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
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
Спасибо Маттео, который отметил проблему с this
-контекст в полнойcallback
, Если исправить это, связав обратный вызов с jQuery.proxy
на каждом узле.
Я добавил издание к коду ранее из Обновления 2.
Обновление 2.2
Это возможная модификация, если вы хотите сделать что-то вроде переключения вращения вперед и назад. Я просто добавил параметр запуска в функцию и заменил эту строку:
$({deg: start}).animate({deg: angle}, args);
Если кто-то знает, как сделать это более общим для всех случаев использования, независимо от того, хотят они установить начальную степень или нет, пожалуйста, внесите соответствующие изменения.
Использование... довольно просто!
В основном у вас есть два способа достичь желаемого результата. Но сначала давайте посмотрим на аргументы:
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 () {});
второй
Я предпочитаю использовать объекты, если существует более трех аргументов, поэтому этот синтаксис - мое преимущество:
$(node).animateRotate(90, {
duration: 1337,
easing: 'linear',
complete: function () {},
step: function () {}
});
Спасибо, 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
});
});
};
Транзит 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 ?
Чтобы сделать этот кросс-браузер, включая 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 пикселей и сделает его полупрозрачным одновременно во время анимации.
Это мое решение:
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' });
Другой ответ, потому что 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});
Без плагина кросс-браузер с 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);