使用jquery.animate()的CSS旋转跨浏览器


81

我正在创建跨浏览器兼容的旋转(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(); 为什么会这样,有没有办法解决?

谢谢。


jQuery不知道如何为旋转设置动画。也许使用CSS3过渡?
John Dvorak

1
@JanDvorak-除了IE9不支持CSS3过渡。
Spudley13年

1
我会为“修复它”部分投票(您可能最终实现了step回调),但是“为什么这样做”部分却很清楚。
John Dvorak

@Spudley:是的,我知道:IE9支持的目标是使用setInterval并多次调用DoRotate函数。
frenchie

顺便说一句-我已经在您对其他问题的回答中指出了CSS沙纸库,它是IE中CSS转换的填充。您可能想尝试一下。
Spudley13年

Answers:


222

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/

而且,顺便说一句:您不需要在jQuery 1.7+之前为CSS3转换添加前缀

更新资料

您可以将其包装在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

我优化了一点,使的顺序easingdurationcomplete微不足道。

$.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,他指出了thiscomplete-中的-context问题callback。如果已修复,则通过在每个节点上将回调与绑定在一起来jQuery.proxy解决。

Update 2之前,我已经将该版本添加到了代码中。

更新2.2

如果您想做一些来回切换旋转的操作,这是一个可能的修改。我只是向该函数添加了一个开始参数,并替换了这一行:

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

如果任何人都知道如何针对所有用例进行通用设置,无论他们是否要设置开始程度,请进行适当的编辑。


用法...非常简单!

主要有两种方法可以达到预期的效果。但是首先,让我们看一下参数:

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

除“ angle”外,它们都是可选的,并回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 () {}
});

4
你能把它摆成小提琴吗?
frenchie

4
好的,很酷:这是跨浏览器(IE9 +)CSS3旋转的插件!!您可以声称:您已经建立了。干得好!
frenchie

1
@matteo对不起,您的回复很晚,感谢您的测试。我需要一点时间来解决这个问题,但是我明白了!fiddle.jshell.net/P5J4V/43顺便说一句,我在我的anwer中提到了您的调查:)
yckart 2014年

1
@matteo之所以this不引用DOM对象,是因为上下文被设置为animate()被调用的对象,在这种情况下{deg: 0}被设置为上下文。您可以通过使用apply()/call()或更改每个回调函数的上下文来解决此问题$.proxy()(如@yckart所示)。这是我解决所有回调并允许3d旋转的解决方案:jsfiddle.net/TrevinAvery/P5J4V/44
Trevin Avery

1
如果要反复为同一元素设置动画,则0每次以度为起点不会导致预期的行为,因此需要使用当前旋转值进行初始化。如何做到这一点在这里解释:stackoverflow.com/a/11840120/61818
阿斯比约恩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-released
yckart

@yckart:1.7.1版本。
lsmpascal

1
@PaxMaximinus正如您在jquery-blog中的文章中所看到的那样,自动前缀只是因为jquery-1.8+
yckart

10

如果您通过jQuery处理CSS3动画,那么jQuery过渡可能会使您的生活更轻松。

EDIT 2014年3月 (因为自从我发布建议以来,我的建议一直在上下投票)

让我解释一下为什么我最初暗示上面的插件:

就性能而言,更新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)'
    }); 
});

有关MDNCSS3过渡的更多详细信息。

但是,还需要记住其他一些事项,如果您具有复杂的动画,链接等内容,那么所有这些操作都会有些棘手,而jQuery Transit只是在后台执行了所有棘手的任务:

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

3

要使用包括IE7 +的跨浏览器,您将需要使用转换矩阵来扩展插件。由于供应商前缀是在jQuery-1.8 +中的jQuery中完成的,因此我将其留给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);
        }
    });
};

注意:如果仅需要设置use或for ,则参数optionsstartAngle是可选的。startAngle{}nulloptions

用法示例:

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度,并在动画中同时将其向右移动100px,并使所有元素变为半透明。


在Firefox中可以使用IE9或IE9,但只能使用Firefox。
利亚姆

好的,现在可以在Chrome,Firefox和IE10中使用。你可以测试IE9,Liam吗?问题在于,Chrome和IE的transform属性未定义,因此脚本认为该transform属性不可用。因此,我改剧本,包括所有的前缀:msowebkitmoz,以确保正确的检测。小提琴也已更新至v12。
Yeti 2014年

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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.