在 JavaScript 中检测和生成 CSS 动画

在编写 催眠螺旋 演示时,出现了一个问题,即我希望在可能的情况下使用 CSS 动画,但有一个回退方案来旋转元素。由于我不想依赖 CSS 动画,因此我认为手动编写它毫无意义,而是在浏览器支持的情况下使用 JavaScript 创建它。以下是如何实现的。

测试动画的支持意味着测试是否支持 style 属性。

var animation = false,
    animationstring = 'animation',
    keyframeprefix = '',
    domPrefixes = 'Webkit Moz O ms Khtml'.split(' '),
    pfx  = '';

if( elm.style.animationName ) { animation = true; }

if( animation === false ) {
  for( var i = 0; i < domPrefixes.length; i++ ) {
    if( elm.style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) {
      pfx = domPrefixes[ i ];
      animationstring = pfx + 'Animation';
      keyframeprefix = '-' + pfx.toLowerCase() + '-';
      animation = true;
      break;
    }
  }
}

[更新 - 之前的代码没有检查浏览器是否支持无前缀的动画 - 这个版本检查了]

这会检查浏览器是否支持任何前缀的动画。如果支持,动画字符串将是 'animation',并且不需要任何关键帧前缀。如果不支持,则遍历所有浏览器前缀(截至目前:))并检查 style 集合上是否存在名为浏览器前缀 + AnimationName 的属性。如果存在,则循环退出,并定义正确的动画字符串和关键帧前缀,并将 animation 设置为 true。在 Firefox 中,这将导致 MozAnimation-moz-,在 Chrome 中导致 WebkitAnimation-webkit- 等等。然后,我们可以使用它在 JavaScript 中创建一个新的 CSS 动画。如果所有前缀检查都没有返回支持的 style 属性,则以替代方式进行动画处理。

if( animation === false ) {

  // animate in JavaScript fallback

} else {
  elm.style[ animationstring ] = 'rotate 1s linear infinite';

  var keyframes = '@' + keyframeprefix + 'keyframes rotate { '+
                    'from {' + keyframeprefix + 'transform:rotate( 0deg ) }'+
                    'to {' + keyframeprefix + 'transform:rotate( 360deg ) }'+
                  '}';

  if( document.styleSheets && document.styleSheets.length ) {

      document.styleSheets[0].insertRule( keyframes, 0 );

  } else {

    var s = document.createElement( 'style' );
    s.innerHTML = keyframes;
    document.getElementsByTagName( 'head' )[ 0 ].appendChild( s );

  }

}

定义了动画字符串后,我们可以在元素上设置(快捷方式)动画。现在,添加关键帧比较棘手。由于它们不是原始动画的一部分,而是与 CSS 语法中分离的(为了提供更大的灵活性和允许重复使用),因此我们无法在 JavaScript 中设置它们。相反,我们需要将它们写成 CSS 字符串。

如果文档中已应用了样式表,则将此关键帧定义字符串添加到该样式表中,如果没有可用的样式表,则使用我们的关键帧创建一个新的样式块并将其添加到文档中。

您可以在 JSFiddle 上查看检测的实际效果和回退 JavaScript 解决方案。

JSFiddle 演示.

非常简单,但也比我最初想象的要复杂一些。您还可以动态检测和更改当前动画,如 Wayne Pan 的这篇文章Joe Lambert 的这个演示 所解释的那样,但这似乎也相当冗长。

例如,我希望有一个 CSSAnimations 集合,您可以在其中以 JSON 或字符串的形式存储不同的动画,并将其名称作为键。目前,动态创建新规则并将其添加到文档中或附加到规则集中似乎是唯一跨浏览器的途径。有什么想法吗?

关于 Chris Heilmann

HTML5 和开放网络的布道者。让我们修复它!

更多 Chris Heilmann 的文章…


5 条评论

  1. Peter van der Zee

    我认为应该是 WebKit(大写 K),尽管它们并不太一致。但我相信他们希望尽可能使用驼峰式大小写版本。

    2011 年 9 月 6 日 03:58

    1. Mook

      实际上,这是一个 CSS 到 JS 的映射问题 - 所有连字符 (-) 都被移除,下一个字符转换为大写,因为 - 不是 JavaScript 属性名称的有效字符。因此,“-webkit-animation-name”变为“WebkitAnimationName”。因此出现了看起来很奇怪的“Khtml”(来自 -khtml)。我不确定为什么 -ms 没有变成“Ms”。这里似乎不是错误;请参阅 -ms-transform(http://msdn.microsoft.com/en-us/library/ff974936%28v=VS.85%29.aspx)以了解类似的情况。也许他们只是再次想变得奇怪……

      2011 年 9 月 6 日 21:35

  2. Gerben

    当浏览器开始放弃前缀表示法而采用官方名称时会发生什么?然后,使用此代码片段的每个站点都将停止显示动画。向前兼容的代码就此结束。

    2011 年 9 月 6 日 09:45

    1. Chris Heilmann

      很好的观点!我通过测试无前缀动画并更改了代码来修复了这个问题。感谢您提出这一点。

      2011 年 9 月 6 日 10:17

  3. Joe Lambert

    这是一个小的 JavaScript shim,用于启用您在帖子末尾建议的那种 JSON 样式接口:http://blog.joelambert.co.uk/2011/09/07/accessing-modifying-css3-animations-with-javascript/

    2011 年 9 月 7 日 02:54

本文的评论已关闭。