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 的动画库。它受到 animateplus 和 animejs 的启发;它专注于性能和开发人员体验,并利用 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.ts
和animate.pug
文件中找到此演示的源代码。是的,演示使用了 Pug,并且设置相当复杂。我强烈建议查看自述文件作为入门指南。
原生计划使用 Gitpod,因此如果您想使用演示,我建议单击 “在 Gitpod 中打开” 链接,因为整个环境已为您设置好——无需进行任何配置。
您还可以查看我整理的 此 CodePen 集合中的更多示例。在大多数情况下,您可以将代码从 animejs 移植到 @okikio/animate,几乎不会遇到任何问题。
我应该提到 @okikio/animate 支持target
和targets
关键字来设置动画目标。@okikio/animate 会将两个目标列表合并为一个列表,并使用Set
删除任何重复的目标。@okikio/animate 支持函数作为动画选项,因此您可以使用类似于 animejs 的交错。 (注意:参数顺序不同,请在自述文件的 “动画选项和 CSS 属性作为方法”部分 中阅读更多内容。)
限制和局限性
@okikio/animate 并不完美;实际上没有什么东西是完美的,并且鉴于 Web Animation API 是一项不断改进的活生生的标准,@okikio/animate 本身仍然有很大的发展空间。也就是说,我一直在努力改进它,并且非常乐意接受您的意见,因此请打开一个新的问题、创建一个拉取请求,或者我们可以在GitHub 项目上进行讨论。
第一个限制是它没有内置的时间轴。这有几个原因:
- 我时间不够。我仍然只是一名学生,没有足够的时间来开发我想要的所有项目。
- 我认为不需要正式的时间轴,因为支持 async/await 编程。此外,我还添加了
timelineOffset
作为动画选项,以防任何人需要创建类似于 animejs 中时间轴的东西。 - 我想使 @okikio/animate 尽可能小。
- 随着组合效果和序列效果即将推出,我认为最好让包保持较小,直到出现实际需求。关于这一点,我强烈建议阅读Daniel C. Wilson 关于 WAAPI 的系列文章,特别是第四部分,其中涵盖了组合效果和序列效果。
@okikio/animate 的另一个限制是它缺乏对自定义缓动函数的支持,例如弹簧、弹性等。但是请查看Jake Archibald 关于缓动工作线程的提案。他讨论了目前正在讨论的多个标准。我更喜欢他的提案,因为它最容易实现,更不用说它是最优雅的了。与此同时,我正在从Kirill Vasiltsov 关于使用 WAAPI 创建弹簧动画的文章中汲取灵感,我计划在库中构建类似的东西。
最后一个限制是 @okikio/animate 仅支持 transform 函数(例如 从 @okikio/[email protected] 开始,情况已不再如此,但对支持颜色的 CSS 属性仍然存在一些限制。查看GitHub 发布以获取更多详细信息。translateX
、translate
、scale
、skew
等)的自动单位。
例如:
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,否则它将无法按预期工作。您还需要为 Map
、Set
和 Promise
添加 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 Patel 在Unsplash 上拍摄。
iOS 上缺乏对
KeyframeEffect.composite
的支持会如何影响它,如果确实如此,它是否提供了解决方法?缺乏
KeyframeEffect.composite
可能会限制您可以创建的复合动画类型,但只要有问题的浏览器支持KeyframeEffect.updateTiming()
和KeyframeEffect.setKeyframes()
,就不会影响@okikio/animate
的任何其他部分。