网络动画 - Chrome 36 中现已提供 element.animate()

网页上的动画曾经是 JavaScript 的专长领域,但随着世界向移动设备转移,动画也转移到了 CSS,因为 CSS 采用了声明式语法,并且浏览器可以使用 CSS 进行优化。既然您始终以在移动设备上实现 60fps 为目标,那么就不要超出浏览器能够高效显示的内容。

越来越多的工具可让 JavaScript 驱动的动画更高效,但终极目标是将声明式动画和命令式动画统一起来,这样,您就可以根据哪种代码最清晰来决定如何编写动画,而不是根据哪种形式可以实现而哪种形式不可以。

Web Animations 有望满足这一需求,其第一部分已以 element.animate() 的形式在 Chrome 36 中发布。借助此新函数,您可以完全使用 JavaScript 创建动画,并且其运行效率与任何 CSS 动画或转场效果一样高(事实上,从 Chrome 34 开始,所有这些方法都是由完全相同的 Web Animations 引擎驱动)。

语法很简单,如果您曾经编写过 CSS 转场效果或动画,应该会对其各部分很熟悉:

element.animate([
    {cssProperty: value0},
    {cssProperty: value1},
    {cssProperty: value2},
    //...
], {
    duration: timeInMs,
    iterations: iterationCount,
    delay: delayValue
});

这项新函数的最大优势在于,我们无需再执行许多繁琐的操作,就能获得流畅无卡顿的动画效果。

例如,在去年的圣诞老人追踪器中,我们希望让雪花不断飘落,因此决定通过 CSS 实现动画效果,以便高效完成

不过,我们希望根据屏幕和场景中正在发生的事件,动态选择雪花的水平位置。当然,在实际运行之前,我们无法知道雪花下落的高度(即用户浏览器窗口的高度)。这意味着,我们真的必须使用 CSS 转场效果,因为在运行时编写 CSS 动画会很快变得复杂(数百个雪花意味着数百条新的样式规则)。

因此,我们采用了以下方法,您应该很熟悉:

snowFlake.style.transform = 'translate(' + snowLeft + 'px, -100%)';
// wait a frame
snowFlake.offsetWidth;
snowFlake.style.transitionProperty = 'transform';
snowFlake.style.transitionDuration = '1500ms';
snowFlake.style.transform = 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)';

关键在于“等待一帧”评论。为了成功启动转换,浏览器必须确认元素位于起始位置。您可以通过以下几种方式来实现此目的。最常见的方法之一是从会强制浏览器计算布局的元素属性中读取,从而确保浏览器知道元素在转换到结束位置之前有一个起始位置。使用此方法,您可以为自己对浏览器内部结构的深入了解而自豪,同时仍然对自己每次按下键盘按键感到不舒服。

相比之下,等效的 element.animate() 调用非常清晰,准确说明了预期用途:

snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);

还有许多其他选项。与 CSS 动画一样,Web 动画也可以延迟和迭代:

snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], {
    duration: 1500,
    iterations: 10,
    delay: 300
});

AnimationPlayer

element.animate() 实际上会返回一个 AnimationPlayer 对象,随着越来越多的 Web 动画规范发布,该对象的重要性将越来越大。无论是使用 JavaScript 还是 CSS 创建的动画,都将具有关联的 AnimationPlayer,以便以有用且有趣的方式无缝组合。

不过,目前 AnimationPlayer 只有两项功能,但都非常实用。您可以随时使用 AnimationPlayer.cancel() 取消动画:

var player = snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);
// less than 1500ms later...changed my mind
player.cancel();

过去,许多人曾尝试围绕 CSS 动画或过渡构建动画系统,但都遇到了各种问题。现在,Web 动画在完成时始终会触发事件,这让他们终于可以松一口气了:

var player = snowFlake.animate([
    {transform: 'translate(' + snowLeft + 'px, -100%)'},
    {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);
player.onfinish = function(e) {
    console.log('per aspera ad terra!');
}

试试看

所有这些功能都将在 Chrome 36 中提供,并于今天进入 Beta 版测试阶段!如果您想试用该功能,请尝试使用 Chrome 36 中的原生实现。不过,有一个 Web 动画 polyfill,可将完整 Web 动画规范的大部分内容引入到任何现代永久更新型浏览器。

您可以试用下雪效果演示,同时使用 element.animate()原生版本和polyfill

请与我们分享您的想法

不过,这只是一项预览功能,我们之所以发布它,是为了立即获得开发者反馈。我们尚不确定自己是否涵盖了所有用例,或者是否磨平了当前动画 API 的所有粗糙边缘。要想知道并真正解决这个问题,唯一的方法就是开发者试用该功能,并告诉我们他们的想法。

当然,对于这篇文章,我们非常欢迎您提供宝贵的意见。对于标准本身,您可以通过 public-fx 邮寄列表向 CSS 和 SVG 工作组提供意见。

更新(2014 年 10 月):Chrome 39 增加了对与控制播放相关的多种其他方法的支持,例如 play()pause()reverse()。它还支持通过 currentTime 属性跳转到动画时间轴中的特定点。您可以在这个新演示中查看此功能的实际运作方式。

感谢 Addy Osmani 和 Max Heinritz 协助撰写这篇文章。