我如何使用WAAPI建立一个动画库

176 阅读11分钟

网络动画API让我们能够构建动画,并通过JavaScript控制其播放。该API向开发者开放了浏览器的动画引擎,并被设计为CSS动画和转场的基础实现,为未来的动画效果敞开了大门。它是在网络上制作动画的最有效的方法之一,让浏览器在没有黑客、胁迫或window.requestAnimationFrame() 的情况下进行自己的内部优化。

有了Web Animations API,我们可以将交互式动画从样式表转移到JavaScript,将表现与行为分开。我们不再需要依赖DOM重的技术,如编写CSS属性和元素的范围类来控制播放方向。而且,与纯粹的、声明式的CSS不同,JavaScript还可以让我们动态地设置属性和持续时间的值。对于建立自定义动画库和创建交互式动画,Web Animations API可能是工作的完美工具。让我们来看看它能做什么!

在本文的其余部分,我有时会把网络动画API称为WAAPI。在搜索网络动画API的资源时,你可能会因为搜索 "网络动画API "而误入歧途,所以,为了方便查找资源,我觉得我们应该采用WAAPI这个术语;在下面的评论中告诉我你的想法。

这是我用WAAPI做的库

@okikio/animate是一个用于现代网络的动画库。它的灵感来自于animateplusanimejs;它专注于性能和开发者体验,并利用Web Animation API以较小的尺寸提供黄油般顺滑的动画,重量约为5.79 KB(已压缩和gzipped)。

@okikio/animate背后的故事

在2020年,我决定做一个更有效的PJAX库,类似于Rezo Zero的--Starting Blocks项目,但有barbajs的易用性我觉得Starting blocks更容易扩展自定义功能,而且可以做得更顺畅、更快速、更容易使用。

**注意:**如果你不知道什么是PJAX库,我建议你去看看MoOx/pjax;简而言之,PJAX允许使用获取请求和切换DOM元素实现页面之间的平滑过渡。

随着时间的推移,我的意图发生了变化,我开始注意到awwwards.com的网站经常使用PJAX,但经常破坏网站和浏览器的自然体验。许多网站第一眼看上去很酷,但实际使用情况往往是另一回事--滚动条经常被覆盖,预取往往过于急切,而且对于没有强大的网络连接、CPU和/或GPU的人来说缺乏准备。因此,我决定逐步加强我将要建立的库。我开始了我所谓的 "原生计划",存储在GitHub repookikio/native中;这是一种以高性能、顺应性和轻量级的方式引入所有酷和现代功能的手段。

在原生计划中,我设计了PJAX库@okikio/native;在一个实际项目的测试中,我遇到了网络动画API,并意识到没有任何库可以利用它,因此,我开发了@okikio/animate,以创建一个符合浏览器的动画库。:这是在2020年,大约在wellyshen开发use-web-animations的同一时间。如果你正在使用react,并且需要一些类似animate.css的快速效果,use-web-animations是一个很好的选择)。)起初,它应该是一个简单的包装器,但是,我一点一点地在它上面做文章,现在它的功能已经和更成熟的动画库相差了80%

**注:**你可以在Github repo okikio/native上阅读更多关于native计划以及@okikio/native库的信息。另外,okikio/native是一个单包,@okikio/native和@okikio/animate是其中的子包。

@okikio/animate在本文中的地位

网络动画API的设计是非常开放的。它本身是有功能的,但它不是对开发者最友好或最直观的API,所以我开发了@okikio/animate来作为WAAPI的一个包装,并将你从其他更成熟的动画库中知道和喜爱的功能(包括一些新的功能)引入到网络动画API的高性能特性中。请阅读项目的README以了解更多的情况。

现在,让我们开始吧

@okikio/animate通过创建Animate的新实例来创建动画(一个作为网络动画API包装的类)。

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

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

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

主动画的存在是为了确保在WAAPI的不同浏览器供应商实现中的准确性。主动画被存储在 [Animate.prototype.mainAnimation](https://github.com/okikio/native/tree/master/packages/animate#mainanimation-animation)中,而目标元素的动画实例则存储在WeakMap ,其关键是其 [KeyframeEffect](https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect).你可以使用"... "访问特定目标的动画。 [Animate.prototype.getAnimation(el)](https://github.com/okikio/native/tree/master/packages/animate#the-long-list-of-get-methods).

你不需要完全理解前面的句子,但它们会帮助你理解@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...");
})();

CodePen Embed Fallback

你也可以用播放控制来玩一个演示。

CodePen Embed Fallback

试试运动路径。

CodePen Embed Fallback

通过改变动画选项来尝试不同类型的运动。

CodePen Embed Fallback

我还创建了一个带有polyfills的复杂演示页面。

查看演示

你可以找到这个演示的源代码在 [animate.ts](https://github.com/okikio/native/blob/master/build/ts/modules/animate.ts)[animate.pug](https://github.com/okikio/native/blob/master/build/pug/animate.pug)文件中找到这个演示的源代码。而且,是的,这个演示使用了Pug,并且是一个相当复杂的设置。我强烈建议你看一下README,作为入门教程

本机倡议使用Gitpod,所以如果你想玩这个演示,我建议点击"在Gitpod中打开 "链接,因为整个环境已经为你设置好了--没有什么需要配置。

你还可以我收集的CodePen中查看更多的例子。大多数情况下,你可以把你的代码从animejs移植到@okikio/animate,几乎没有问题。

我应该提到,@okikio/animate支持targettargets 两个关键字来设置动画目标。@okikio/animate会将两个目标列表合并为一个列表,并使用 [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set)s来删除任何重复的目标。@okikio/animate支持函数作为动画选项,所以你可以使用类似于animejs的交错方式。(注意:参数的顺序是不同的,在README文件的 "动画选项和作为方法的CSS属性 "部分阅读更多内容)。

限制和约束

@okikio/animate并不完美;没有什么是真正完美的,而且看到网络动画API是一个不断改进的生活标准,@okikio/animate本身仍有很大的发展空间。也就是说,我一直在努力改进它,并希望得到你的意见,所以请你开一个新问题,创建一个拉动请求,或者我们可以在GitHub项目中进行讨论。

第一个限制是,它没有一个真正的内置时间表。这有几个原因。

  1. 我没有时间了。我还只是个学生,没有很多时间来开发所有我想开发的项目。
  2. 我认为不需要正式的时间线,因为支持异步/等待编程。此外,我还添加了 [timelineOffset](https://github.com/okikio/native/tree/master/packages/animate#timelineoffset)作为一个动画选项,如果有人需要在animejs中创建类似于时间线的东西。
  3. 我想让@okikio/animate尽可能的小。
  4. 由于群组效果序列效果即将到来,我认为在实际需要出现之前,最好让软件包小一点。关于这一点,我强烈建议阅读Daniel C. Wilson关于WAAPI的系列文章,尤其是第四部分,其中涉及到了群组效果和序列效果。

@okikio/animate的另一个局限性是,它缺乏对自定义动画的支持,如弹簧、弹性等。但可以看看Jake Archibald关于easing worklet的建议。他讨论了目前正在讨论的多个标准。我更喜欢他的提议,因为这是最容易实现的,更不用说是最优雅的。同时,我从Kirill Vasiltsov关于WAAPI的Spring动画的文章中获得了灵感我正计划在库中建立类似的东西。

最后一个限制是@okikio/animate只支持转换函数的自动单位,例如:translateX,translate,scale,skew, 等等。从@okikio/animate@2.2.0 开始,这种情况不再存在,但对支持颜色的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中建立了时间线动画选项,以保证它的未来性。这里有一个例子。

CodePen嵌入回退

感谢Bramus提供的演示灵感!另外,你可能需要Canary版本的Chrome浏览器,或者需要在Chrome标志中打开实验性网络平台功能来观看这个演示。不过,它在火狐浏览器上似乎运行良好,所以......🤣。

如果你想阅读更多关于ScrollTimeline的信息,Bramus写了一篇很好的文章。我也建议你阅读谷歌开发者关于Animation Worklets的文章

我希望能让这个库变得更小。目前它是~5.79 KB,至少在我看来是很高的。通常情况下,我会使用bundlephobia嵌入,但它在捆绑项目上有困难,所以如果你想验证大小,我建议使用bundle.js.org,因为它实际上是在你的浏览器上捆绑代码。我专门为检查@okikio/animate的捆绑大小而建立了它,但请注意它不像bundlephobia那样准确。

多重填充

早期的一个演示展示了polyfills的作用。你将需要 [web-animations-next.min.js](https://github.com/web-animations/web-animations-js)来支持时间线。其他的现代功能,KeyframeEffect 构造函数是需要的。

polyfill使用JavaScript来测试KeyframeEffect 是否被支持,如果不被支持,polyfill就会加载并完成其工作。只要避免在polyfill中添加async/defer就可以了,否则它将不会按照你所期望的方式工作。你还需要 polyfillMap,Set, 和Promise

<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(关闭minify)、Typescript(以ES5为目标)和terser;就目前而言,这是转译到ES5的最快设置,它比babel更快、更可靠。更多细节请看演示中的Gulp文件

结论

@okikio/animate是一个围绕网络动画API(WAAPI)的包装器,它允许你在一个小而简洁的包装中使用你喜欢的animejs和其他动画库的所有功能。那么,在读完它之后,你有什么想法?当你需要制作复杂的动画时,你认为你会去使用它吗?或者,更重要的是,是否有什么东西会阻碍你使用它?请在下面留言或加入Github讨论区的讨论。


这篇文章最初出现在dev.to上,也出现在hackernoon.comhashnode.com上。
照片由Pankaj PatelUnsplash上拍摄。


The postHow I Used the WAAPI to Build an Animation Libraryappeared first onCSS-Tricks.

你可以通过成为MVP支持者来支持CSS-Tricks。