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

布兰登·肯尼
Brendan Kenny

网络动画曾经是 JavaScript 的产物,但随着世界转向移动设备,动画已迁移到 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 类似,网络动画也可以延迟和迭代:

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

AnimationPlayer

element.animate() 实际上会返回 AnimationPlayer 对象,随着越来越多的网页动画规范的推出,该对象变得越来越重要。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 动画或过渡构建动画系统的所有人来说,让每一个人都松了一口气,网络动画在完成时总会触发事件:

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 Animations polyfill,它可以将完整 Web Animations 规范中所占的比重大幅增加到所有现代、经典的浏览器。

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

请与我们分享您的想法

事实上,这里只是对即将推出的功能进行了预览,发布的目的是为了立即获取开发者的反馈。我们现在还不确定我们是否涵盖了所有用例,或者打磨了当前动画 API 的每个粗糙边缘。我们了解并真正做到这一点的唯一方法是让开发者试用,然后告诉我们他们的想法。

这篇博文的评论当然很有价值,您可以通过 public-fx 邮寄名单将对标准本身的评论发送给 CSS 和 SVG 工作组。

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

感谢 Addy Osmani 和 Max Heinritz 提供这篇博文的帮助。