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

Brendan Kenny
Brendan Kenny

网页上的动画曾经是 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 协助撰写这篇文章。