【问题标题】:CSS rotation cross browser with jquery.animate()带有 jquery.animate() 的 CSS 旋转跨浏览器
【发布时间】:2013-02-17 22:06:44
【问题描述】:

我正在创建一个跨浏览器兼容的旋转 (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 过渡?
  • @JanDvorak - 除了 IE9 不支持 CSS3 过渡。
  • 我会赞成“修复它”部分(您最终可能会实现 step 回调),但“为什么会这样”部分非常清楚。
  • @Spudley:是的,我知道:IE9 支持的目标是使用 setInterval 并多次调用 DoRotate 函数。
  • 顺便说一句 - 我已经在我对你的另一个问题的回答中指出了 CSS Sandpaper 库,它是 IE 中 CSS 转换的 polyfill。您可能想尝试一下。

标签: jquery css rotation jquery-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/

而且,顺便说一句:您不需要为 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,他注意到完整-callback 中的this-context 存在问题。如果通过绑定每个节点上带有jQuery.proxy 的回调来修复它。

我在 Update 2 之前已将版本添加到代码中。

更新 2.2

如果您想要执行诸如来回切换旋转之类的操作,这是一个可能的修改。我只是在函数中添加了一个 start 参数并替换了这一行:

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

【讨论】:

  • 你能把这个放在小提琴里吗?
  • 好的,非常酷:这是跨浏览器 (IE9+) CSS3 旋转的插件!你可以声称:你建造了那个。干得好!
  • @matteo 抱歉回复晚了,感谢您的测试。我需要一点时间来解决这个问题,但我明白了! fiddle.jshell.net/P5J4V/43顺便说一句,我在回答中提到了你的调查:)
  • @matteo this 不引用 DOM 对象的原因是因为上下文设置为对象 animate() 被调用,在这种情况下 {deg: 0} 设置为上下文。您可以通过使用apply()/call()$.proxy() 更改每个回调函数的上下文来解决此问题(如@yckart 所示)。这是我修复所有回调并允许 3d 旋转的解决方案:jsfiddle.net/TrevinAvery/P5J4V/44
  • 如果你想一遍又一遍地为同一个元素设置动画,每次从0度开始不会导致预期的行为,所以你需要用当前的旋转值进行初始化。如何做到这一点在这里解释:stackoverflow.com/a/11840120/61818
【解决方案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
        });
    });
};

【讨论】:

  • jQuery 自动添加需要的供应商前缀,所以不需要这个!
  • +1 表示跨平台。伟大的。 @yckart:在这种情况下,自动前缀对我不起作用。
  • @PaxMaximinus 您使用什么 jQuery 版本? blog.jquery.com/2012/08/09/jquery-1-8-released
  • @yckart : 1.7.1 版本。
  • @PaxMaximinus 正如您在 jquery-blog 的文章中看到的那样,自动前缀只是因为 jquery-1.8+!
【解决方案3】:

如果您通过 jQuery 处理 CSS3 动画,jQuery transit 可能会让您的生活更轻松。

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

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

在每一步更新DOM(即$.animate)在性能方面并不理想。 它可以工作,但很可能比纯 CSS3 transitionsCSS3 animations 慢。

这主要是因为如果您指出从开始到结束的过渡效果,浏览器就有机会提前思考。

为此,例如,您可以为过渡的每个状态创建一个 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 transitions on MDN 的详细信息。

但是还有一些其他的事情要记住,如果你有复杂的动画、链接等,这一切都会变得有点棘手,而jQuery Transit 只是在引擎盖:

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

【讨论】:

    【解决方案4】:

    要实现包括 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);
            }
        });
    };
    

    注意: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 度,并将其向右移动 100 像素,并在动画期间同时使其半透明。

    【讨论】:

    • 或 IE9,在 Firefox 中可以,但只有 firefox。
    • 好的,它现在可以在 Chrome、Firefox 和 IE10 中使用。 Liam,你能测试一下 IE9 吗?问题是 Chrome 和 IE 的转换属性未定义,因此脚本认为转换属性不可用。因此,我更改了脚本以包含所有前缀:msowebkitmoz 以确保正确检测。小提琴也更新到 v12。
    【解决方案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);
    };
    

    那么你就可以在默认的 animate 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);
        

        【讨论】:

          猜你喜欢
          • 2012-08-03
          • 2012-09-30
          • 1970-01-01
          • 1970-01-01
          • 2012-05-05
          • 2015-03-07
          • 2020-11-27
          • 1970-01-01
          相关资源
          最近更新 更多