Chromium 84 中的 Web Animations API 改进

使用 Promise 编排动画、通过可替换动画提升性能、通过复合模式实现更流畅的动画等。

Kevin Ellis
Kevin Ellis

发布日期:2020 年 5 月 27 日

正确使用动画可提高用户对品牌的认知度和记忆力,引导用户操作,并帮助用户浏览应用,从而在数字空间中提供上下文。

Web Animations API 是一款工具,可让开发者使用 JavaScript 编写命令式动画。它旨在为 CSS 动画和过渡实现提供基础,并支持开发未来的效果,以及组合和定时现有效果。

虽然 FirefoxSafari 已实现了完整的规范功能,但 Chromium 84 为 Chrome 和 Edge 带来了一系列之前不支持的功能,从而实现了跨浏览器互操作性。

Web Animations API 最早在 2014 年 7 月的 Chromium 36 版中发布。现在,该规范将在 2020 年 7 月发布的版本 84 中完成。
Web Animations API 在 Chromium 中的悠久历史。

使用入门

如果您使用过 @keyframe 规则,使用 Web Animations API 创建动画应该会非常熟悉。首先,您需要创建一个关键帧对象。在 CSS 中,代码可能如下所示:

@keyframes openAnimation {
  0% {
    transform: scale(0);
  }
  100% {
    transform: scale(1);
  }
}

如下所示:

const openAnimation = [
  { transform: 'scale(0)' },
  { transform: 'scale(1)' },
];

在 CSS 中设置动画参数的位置:

.modal {
  animation: openAnimation 1s 1 ease-in;
}

您可以在 JS 中进行设置:

document.querySelector('.modal').animate(
    openAnimation, {
      duration: 1000, // 1s
      iterations: 1, // single iteration
      easing: 'ease-in' // easing function
    }
);

代码量大致相同,但使用 JavaScript,您可以获得 CSS 无法提供的强大功能。这包括能够对音效进行排序,以及增强对播放状态的控制。

element.animate()以外

不过,在更新后,Web Animations API 不再局限于使用 element.animate() 创建的动画。我们也可以操纵 CSS 动画和过渡。

getAnimations() 是一种方法,用于返回元素上的所有动画,无论该元素是使用 element.animate() 还是使用 CSS 规则(CSS 动画或转场效果)创建的。其外观如下:

您首先"get"转换的关键帧,以确定转换的起始位置。然后,您将创建两个新的不透明度动画,以实现交叉淡出效果。交叉淡化完成后,您可以删除副本。

如何使用 Promise 编排动画

在 Chromium 84 中,您现在可以使用两种可与 promise 搭配使用的方法:animation.readyanimation.finished

  • animation.ready 可让您等待待处理的更改生效(即在播放控制方法 [例如播放和暂停] 之间切换)。
  • animation.finished 提供了一种在动画完成时执行自定义 JavaScript 代码的方法。

继续我们的示例,使用 animation.finished 创建一个协调的动画链。在这里,您有垂直转换 (scaleY),后跟水平转换 (scaleX),最后是子元素的透明度更改:

对起始模态元素应用转换和不透明度。查看 Codepen 上的演示
const transformAnimation = modal.animate(openModal, openModalSettings);
transformAnimation.finished.then(() => { text.animate(fadeIn, fadeInSettings)});

在执行链中的下一个动画集之前,我们使用 animation.finished.then() 将这些动画串联起来。这样,动画就会按顺序显示,您甚至可以为不同的目标元素应用不同的效果设置(例如速度和缓动)。

在 CSS 中,重新创建此类动画会很麻烦,尤其是在将唯一但顺序的动画应用于多个元素时。您必须使用 @keyframe,整理出正确的时间百分比以放置动画,并在序列中触发动画之前使用 animation-delay

示例:播放、暂停和倒放

什么能打开,应该关闭!幸运的是,自 Chromium 39 发布以来,Web Animations API 便已让我们具备播放、暂停和倒退动画的功能。

您可以使用 .reverse() 获取之前显示的动画,并在再次点击按钮时为其提供流畅的反向动画。这样,你就能为模态窗口创建更顺畅、更符合情境的互动。

点击按钮时模态窗口打开和关闭的示例。查看 Glitch 演示

您可以创建两个待播放的动画(openModal 和内嵌不透明度转换),然后暂停其中一个动画,使其延迟到另一个动画结束。然后,您可以使用 promise 等待每个 promise 都完成,然后再播放。最后,您可以检查是否已设置标记,然后反转每个动画。

示例:使用部分关键帧的动态互动

重新定位示例:鼠标点击会将动画调整到新位置。在 Glitch 上查看演示
selector.animate([{transform: `translate(${x}px, ${y}px)`}],
    {duration: 1000, fill: 'forwards'});

在此示例中,只有一个关键帧,没有指定的起始位置。这是使用部分关键帧的示例。鼠标处理脚本会执行以下操作:设置新的结束位置并触发新的动画。系统会根据当前的底层位置推断出新的起始位置。

现有转换仍在运行的同时可触发新的转换。这意味着当前转换会中断,系统会创建一个新的转换。

通过可替换动画提升了性能

基于事件(例如 'mousemove')创建动画时,系统会每次都创建一个新动画,这可能会快速耗尽内存并降低性能。为了解决此问题,Chromium 83 中引入了可替换动画,以实现自动清理,其中已完成的动画会被标记为可替换,并在被其他已完成的动画替换后自动移除。请参考以下示例:

当鼠标指针移动时,彗星的轨迹以动画形式呈现。在 Glitch 上查看演示
elem.addEventListener('mousemove', evt => {
  rectangle.animate(
    { transform: translate(${evt.clientX}px, ${evt.clientY}px) },
    { duration: 500, fill: 'forwards' }
  );
});

每次鼠标移动时,浏览器都会重新计算彗星尾迹中每个球的位置,并为此新点创建动画。现在,浏览器知道在以下情况下移除旧动画(启用替换):

  1. 动画播放完毕。
  2. 在复合排序中,有一个或多个动画也已完成。
  3. 新动画会为相同的属性添加动画效果。

通过使用 anim.onremove 触发计数器,您可以计算每个已移除的动画的计数器,从而确切了解被替换的动画数量。

您还可以通过以下几种方法进一步控制动画:

  • animation.replaceState() 提供了一种跟踪动画是处于有效、持久化还是已移除状态的方法。
  • animation.commitStyles() 会根据底层样式以及元素上的所有动画(按复合顺序)更新元素的样式。
  • animation.persist() 会将动画标记为不可替换。

使用复合模式实现更流畅的动画

借助 Web Animations API,您现在可以设置动画的复合模式,这意味着除了默认模式“replace”之外,动画还可以是累加模式或累积模式。借助复合模式,开发者可以编写独特的动画,并控制效果的组合方式。现在支持三种复合模式:'replace'(默认模式)、'add''accumulate'

在合成动画时,开发者可以编写简短的独特效果,并查看它们的组合效果。在以下示例中,我们将旋转和缩放关键帧应用于每个框,唯一的调整是作为选项添加的复合模式:

展示默认、添加和累积复合模式的演示。在 Glitch 上查看演示

在默认的 'replace' 复合模式下,最终动画会替换转换属性,并在 rotate(360deg) scale(1.4) 处结束。对于 'add',复合会添加旋转并乘以缩放,最终状态为 rotate(720deg) scale(1.96)'accumulate' 会组合这些转换,从而生成 rotate(720deg) scale(1.8)。如需详细了解这些复合模式的复杂性,请参阅 Web 动画规范中的 CompositeOperation 和 CompositeOperationOrAuto 枚举

请查看以下界面元素示例:

一个弹跳的下拉菜单,其中应用了两个复合动画。在 Glitch 上查看演示

此处合成了两个 top 动画。第一种是宏动画,将下拉菜单以菜单本身的整个高度作为滑入效果从页面顶部移动;第二种是微动画,在点击底部时应用一点弹跳。使用 'add' 复合模式可实现更流畅的转换。

const dropDown = menu.animate(
    [
      { top: `${-menuHeight}px`, easing: 'ease-in' },
      { top: 0 }
    ], { duration: 300, fill: 'forwards' });

  dropDown.finished.then(() => {
    const bounce = menu.animate(
      [
        { top: '0px', easing: 'ease-in' },
        { top: '10px', easing: 'ease-out' },
        { ... }
      ], { duration: 300, composite: 'add' });
  });

Web Animations API 的未来发展

这些都是现有浏览器动画功能的令人兴奋的补充,我们还将推出更多动画功能。如需详细了解后续动态,请参阅以下未来规范: