如何使用 WAAPI 构建动画库

Avatar of Okiki Ojo
Okiki Ojo 发表

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

Web 动画 API 允许我们构建动画并使用 JavaScript 控制其播放。该 API 将浏览器的动画引擎开放给开发人员,旨在作为 CSS 动画和过渡实现的基础,为未来的动画效果敞开大门。它是网络上性能最高的动画方式之一,允许浏览器进行自己的内部优化,无需任何 hack、强制或window.requestAnimationFrame()

使用 Web 动画 API,我们可以将交互式动画从样式表移动到 JavaScript,从而将表示与行为分离。我们不再需要依赖于 DOM 密集型技术,例如编写 CSS 属性和将类作用域应用于元素以控制播放方向。与纯声明式 CSS 不同,JavaScript 还允许我们动态地设置从属性到持续时间的各种值。对于构建自定义动画库和创建交互式动画,Web 动画 API 可能是完美的工具。让我们看看它能做什么!

在本文的其余部分,我将有时将 Web 动画 API 称为 WAAPI。在搜索 Web 动画 API 资源时,搜索“Web 动画 API”可能会误导您,因此,为了方便查找资源,我认为我们应该采用 WAAPI 这个术语;请在下面的评论中告诉我您的想法。

这是我使用 WAAPI 创建的库

@okikio/animate 是一个适用于现代 Web 的动画库。它受到 animateplusanimejs 的启发;它专注于性能和开发人员体验,并利用 Web 动画 API 以较小的体积提供流畅的动画,压缩后大小约为 ~5.79 KB

@okikio/animate 背后的故事

2020 年,我决定创建一个更高效的 PJAX 库,类似于 Rezo Zero 的 – Starting Blocks 项目,但具有 barbajs 的易用性。我觉得 Starting Blocks 更容易扩展自定义功能,并且可以变得更流畅、更快、更易于使用。

注意:如果您不知道什么是 PJAX 库,我建议查看 MoOx/pjax;简而言之,PJAX 允许使用 fetch 请求和切换 DOM 元素在页面之间平滑过渡。

随着时间的推移,我的意图发生了变化,我开始注意到 awwwards.com 上的网站有多频繁地使用 PJAX,但往往会破坏网站和浏览器的自然体验。许多网站乍一看很酷,但实际使用通常会讲述不同的故事——滚动条经常被覆盖,预取经常过于积极,并且缺乏为没有强大网络连接、CPU 和/或 GPU 的用户做好准备。因此,我决定逐步增强我将要构建的库。我开始了我称之为“原生计划”的内容,存储在 GitHub 存储库 okikio/native 中;一种以高性能、合规和轻量级的方式引入所有酷炫和现代功能的方法。

对于原生计划,我设计了 PJAX 库 @okikio/native;在实际项目中进行测试时,我遇到了 Web 动画 API,并意识到没有利用它的库,因此,我开发了 @okikio/animate,以创建一个浏览器兼容的动画库。(注意:这是在 2020 年,大约在 wellyshen 开发 use-web-animations 的同时。如果您正在使用 React 并需要一些快速类似 animate.css 的效果,use-web-animations 是一个不错的选择。)最初,它应该是一个简单的包装器,但渐渐地,我不断构建它,现在它在功能上与更成熟的动画库的相似度达到了80%

注意:您可以在 Github 存储库 okikio/native 上了解更多关于原生计划以及 @okikio/native 库的信息。此外,okikio/native 是一个 monorepo,其中 @okikio/native 和 @okikio/animate 是其中的子包。

@okikio/animate 在本文中的作用

Web 动画 API 在设计上非常开放。它本身功能强大,但并不是最友好的或最直观的 API,因此我开发了 @okikio/animate 作为 WAAPI 的包装器,并将您在其他更成熟的动画库中熟悉并喜爱的功能(包括一些新功能)引入到 Web 动画 API 的高性能特性中。阅读项目的自述文件以获取更多上下文。

现在,让我们开始吧

@okikio/animate 通过创建 Animate 的新实例(一个充当 Web 动画 API 包装器的类)来创建动画。

import { Animate } from"@okikio/animate";

new Animate({
  target: [/* ... */],
  duration: 2000,
  // ... 
});

Animate 类接收一组要进行动画的目标,然后创建一系列 WAAPI 动画实例,以及一个 主动画(主动画是一个小的动画实例,设置为在一个不可见的元素上进行动画,它作为跟踪各种目标元素的动画进度的一种方式存在),Animate 类然后播放每个目标元素的动画实例,包括主动画,以创建流畅的动画。

主动画是为了确保 WAAPI 在不同浏览器供应商实现中的准确性。主动画存储在Animate.prototype.mainAnimation 中,而目标元素的动画实例存储在 WeakMap 中,其键是其KeyframeEffect。您可以使用Animate.prototype.getAnimation(el)访问特定目标的动画。

您不需要完全理解前面的句子,但它们将有助于您理解 @okikio/animate 的作用。如果您想了解有关 WAAPI 工作原理的更多信息,请查看 MDN,或者如果您想了解有关 @okikio/animate 库的更多信息,我建议您查看 GitHub 上的 okikio/native 项目。

用法、示例和演示

默认情况下,创建 Animate 的新实例非常麻烦,因此,我创建了animate 函数,它在每次调用时都会创建新的 Animate 实例。

import animate from "@okikio/animate";
// or
import { animate } from "@okikio/animate";

animate({ 
  target: [/* ... */],
  duration: 2000,
  // ... 
});

当使用 @okikio/animate 库创建动画时,您可以这样做

import animate from "@okikio/animate";

// Do this if you installed it via the script tag: const { animate } = window.animate;

(async () => {
  let [options] = await animate({
    target: ".div",

    // Units are added automatically for transform CSS properties
    translateX: [0, 300],
    duration: 2000, // In milliseconds
    speed: 2,
  });

  console.log("The Animation is done...");
})();

您还可以使用带有播放控件的演示进行体验

尝试运动路径

通过更改动画选项尝试不同的运动类型

我还创建了一个包含 polyfill 的复杂演示页面

您可以在 GitHub 存储库中的animate.tsanimate.pug 文件中找到此演示的源代码。是的,演示使用了 Pug,并且设置相当复杂。我强烈建议查看自述文件作为入门指南

原生计划使用 Gitpod,因此如果您想使用演示,我建议单击 “在 Gitpod 中打开” 链接,因为整个环境已为您设置好——无需进行任何配置。

您还可以查看我整理的 此 CodePen 集合中的更多示例。在大多数情况下,您可以将代码从 animejs 移植到 @okikio/animate,几乎不会遇到任何问题。

我应该提到 @okikio/animate 支持targettargets 关键字来设置动画目标。@okikio/animate 会将两个目标列表合并为一个列表,并使用Set删除任何重复的目标。@okikio/animate 支持函数作为动画选项,因此您可以使用类似于 animejs 的交错。 (注意:参数顺序不同,请在自述文件的 “动画选项和 CSS 属性作为方法”部分 中阅读更多内容。)

限制和局限性

@okikio/animate 并不完美;实际上没有什么东西是完美的,并且鉴于 Web Animation API 是一项不断改进的活生生的标准,@okikio/animate 本身仍然有很大的发展空间。也就是说,我一直在努力改进它,并且非常乐意接受您的意见,因此请打开一个新的问题、创建一个拉取请求,或者我们可以在GitHub 项目上进行讨论。

第一个限制是它没有内置的时间轴。这有几个原因:

  1. 我时间不够。我仍然只是一名学生,没有足够的时间来开发我想要的所有项目。
  2. 我认为不需要正式的时间轴,因为支持 async/await 编程。此外,我还添加了timelineOffset 作为动画选项,以防任何人需要创建类似于 animejs 中时间轴的东西。
  3. 我想使 @okikio/animate 尽可能小。
  4. 随着组合效果序列效果即将推出,我认为最好让包保持较小,直到出现实际需求。关于这一点,我强烈建议阅读Daniel C. Wilson 关于 WAAPI 的系列文章,特别是第四部分,其中涵盖了组合效果和序列效果。

@okikio/animate 的另一个限制是它缺乏对自定义缓动函数的支持,例如弹簧、弹性等。但是请查看Jake Archibald 关于缓动工作线程的提案。他讨论了目前正在讨论的多个标准。我更喜欢他的提案,因为它最容易实现,更不用说它是最优雅的了。与此同时,我正在从Kirill Vasiltsov 关于使用 WAAPI 创建弹簧动画的文章中汲取灵感,我计划在库中构建类似的东西。

最后一个限制是 @okikio/animate 仅支持 transform 函数(例如 translateXtranslatescaleskew 等)的自动单位。从 @okikio/[email protected] 开始,情况已不再如此,但对支持颜色的 CSS 属性仍然存在一些限制。查看GitHub 发布以获取更多详细信息。

例如:

animate({
  targets: [".div", document.querySelectorAll(".el")],

  // By default "px", will be applied
  translateX: 300,
  left: 500,
  margin: "56 70 8em 70%",

  // "deg" will be applied to rotate instead of px
  rotate: 120, 

  // No units will be auto applied
  color: "rgb(25, 25, 25)",
  "text-shadow": "25px 5px 15px rgb(25, 25, 25)"
});

展望未来

一些未来的功能,例如ScrollTimeline,即将到来。我认为没有人真正知道它何时发布,但自从 Chrome Canary 92 中出现 ScrollTimeline 以来,我认为可以肯定地说,在不久的将来发布的可能性非常大。

我在 @okikio/animate 中构建了timeline 动画选项以使其面向未来。这是一个示例:

感谢 Bramus 提供的演示灵感!此外,您可能需要 Chrome 的 Canary 版本或需要在 Chrome 标志中打开实验性 Web 平台功能才能查看此演示。不过,它在 Firefox 上似乎运行良好,所以……🤣。

如果您想了解更多关于 ScrollTimeline 的信息,Bramus 撰写了一篇关于它的优秀文章。我还建议阅读 Google Developers 关于动画工作线程的文章。

我希望使库更小。它目前为约 5.79 KB,这在我看来似乎有点大。通常,我会使用bundlephobia 嵌入,但它在捆绑项目时遇到问题,因此,如果您想验证大小,我建议使用bundle.js.org,因为它实际上在您的浏览器上本地捆绑代码。我专门为检查 @okikio/animate 的包大小而构建了它,但请注意,它不如 bundlephobia 精确。

Polyfills

较早的演示之一展示了 polyfills 的作用。您将需要来自 web-animations-js 的web-animations-next.min.js 来支持时间轴。其他现代功能需要 KeyframeEffect 构造函数。

polyfill 使用 JavaScript 测试是否支持 KeyframeEffect,如果不支持,则 polyfill 加载并执行其操作。只需避免向 polyfill 添加 async/defer,否则它将无法按预期工作。您还需要为 MapSetPromise 添加 polyfill。

<html>
  <head>
    <!-- Async -->
    <script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=default,es2015,es2018,Array.prototype.includes,Map,Set,Promise" async></script>
    <!-- NO Async/Defer -->
    <script src="./js/webanimation-polyfill.min.js"></script>
  </head>
  <body>
    <!-- Content -->
  </body>
</html>

如果您正在为 ES6+ 构建,我强烈建议使用esbuild 进行转译、捆绑和缩小。对于 ES5,我建议使用 esbuild(关闭缩小)、Typescript(目标为 ES5)和terser;截至目前,这是转译到 ES5 的最快设置,它比 babel 更快、更可靠。请参阅演示的Gulpfile以获取更多详细信息。

结论

@okikio/animate 是 Web Animation API (WAAPI) 的一个包装器,它允许您在一个小巧简洁的包中使用来自 animejs 和其他动画库的所有您喜欢的功能。那么,在阅读完有关它的内容后,您有什么想法?您认为在需要创建复杂动画时会用到它吗?或者,更重要的是,有什么会阻止您使用它?请在下面发表评论或加入Github 讨论


本文最初出现在 dev.to 上,也出现在hackernoon.com 和我的博客blog.okikio.dev上。
照片由Pankaj PatelUnsplash 上拍摄。