Chromium 84 中 Web Animations API 的改进

使用 Promise 编排动画、可替换动画带来的性能改进、复合模式带来的更流畅动画等等。

发布时间:2020 年 5 月 27 日

如果使用得当,动画可以提升用户对您品牌的感知和记忆,引导用户操作,并帮助用户浏览您的应用程序,从而在数字空间中提供上下文。

Web Animations API 是一种工具,使开发者能够使用 JavaScript 编写命令式动画。它的编写目的是为了支持 CSS 动画和过渡效果的实现,并支持未来效果的开发,以及现有效果的组合和定时。

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

The Web Animations API first hit Chromium in version 36, July of 2014. Now the spec is going to be complete, in version 84, launching July 2020.
Web Animations API 在 Chromium 中的漫长历史。

入门指南

如果您使用过 @keyframe 规则,那么使用 Web Animations API 创建动画应该会感到非常熟悉。首先,您需要创建一个关键帧对象。在 CSS 中,它可能看起来像这样

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

在 JavaScript 中,它看起来会像这样

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 等待每个动画完成再播放。最后,您可以检查是否设置了标志,然后反转每个动画。

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

重定向示例,其中鼠标单击将动画调整到新位置。请参阅 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'

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

一个演示默认、add 和 accumulate 复合模式的演示。请参阅 Glitch 上的演示

在默认的 'replace' 复合模式下,最终动画会替换 transform 属性,并最终达到 rotate(360deg) scale(1.4)。对于 'add',复合模式会添加旋转并乘以缩放,从而产生 rotate(720deg) scale(1.96) 的最终状态。'accumulate' 组合变换,从而产生 rotate(720deg) scale(1.8)。有关这些复合模式的复杂性的更多信息,请查看 Web Animations 规范中的 CompositeOperation 和 CompositeOperationOrAuto 枚举

看看以下 UI 元素示例

一个弹跳式下拉菜单,它应用了两个复合动画。请参阅 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 的未来发展方向

这些都是当今浏览器动画功能的令人兴奋的新增功能,并且还有更多新增功能正在开发中。查看以下未来规范,进一步了解接下来的发展方向