Angular 17+ 高级教程 – Animation 动画
前言
Angular 有一套 built-in 的 Animation 方案。这套方案的底层实现是基于游览器原生的 Web Animation API。
CSS Transition -> CSS Animation -> Web Animation API -> Angular Animation 这 4 套方案的关系并不是互相替代,而是互相弥补。
越靠后的方案就越复杂,所以对于使用者来说,如果不是真的有必要,应该要尽可能使用靠前面的方案。
本篇会假设你对 CSS Transition、CSS Animation、Web Animation API 已经熟悉,会在这些基础之上讲解 Angular Animation。
不熟悉它们的朋友可以先看下面这两篇文章。
- CSS – Transition & Animation
-
DOM – Web Animation API
Angular Animation 虽然是对原生 Web Animation API 的封装和扩展,但 Angular 并没有完全公开所有 Web Animation API 的接口,
也就是说,我们不一定可以使用 Angular Animation 做出 Web Animation API 的效果,或者说使用 Angular Animation 可能会更难达到效果。
所以在面对不同动画需求时,我们一定要慎选方案。
本篇将从 Angular Animation 比较底层的使用方式开始,大致对比 Angular Animation 和 Web Animation API 的区别,看看它们的共同点和不同之处。
然后在深入 Angular Animation 独有的功能和日常使用方式,最后微微逛一下源码,开始吧 🚀。
AnimationBuilder
AnimationBuilder 是一个 Root Level Dependancy Injection Provider。
它比较底层,日常开发中很少会用到,但是从它开始理解 Angular Animation 会比较轻松,所以我们从它开始。
搭环境
我们搭个测试环境,对比 Angular Animation 和 Web Animation API。
app.config.ts
import { type ApplicationConfig } from '@angular/core';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
export const appConfig: ApplicationConfig = {
providers: [provideAnimationsAsync()],
};
要使用 Angular Animation 需要提供 Provider。
注:虽然方法名 endswith Async,不过它不是异步,也不返回 Promise 哦,只是名字而已。
App Template
<div class="box-list" > <div #nativeBox class="box"></div> <div #ngBox class="box"></div> </div>
两个 div box 做 compare。
第一个是 native box,将使用 Web Animation API,
第二个是 ng box,将使用 Angular Animation。
App Styles
:host { display: block; padding: 56px; } .box-list { display: flex; flex-direction: column; gap: 16px; } .box { width: 100px; height: 100px; background-color: red; }
效果

The Simplest Aniamtion
我们为 2 个 box 添加一个简单的 animation。
native box 使用 Web Animation API,ng box 使用 Angular Animation。
App 组件
export class AppComponent { // 1. query both boxes readonly nativeBox = viewChild.required<string, ElementRef<HTMLElement>>('nativeBox', { read: ElementRef }); readonly ngBox = viewChild.required<string, ElementRef<HTMLElement>>('ngBox', { read: ElementRef }); constructor() { // 2. inject AnimationBuilder const builder = inject(AnimationBuilder); afterNextRender(() => { // 3. delay 1 second (只是为了比较容易看效果) window.setTimeout(() => { // 4. get box HTMLElement const nativeBox = this.nativeBox().nativeElement; const ngBox = this.ngBox().nativeElement; // 5. use Web Aniamtion API for native box const nativePlayer = nativeBox.animate([{ width: '200px', backgroundColor: 'yellow' }], { duration: 1000, fill: 'forwards', easing: 'ease', }); // 6. use Angular Animation for ng box const factory = builder.build([animate('1s ease', style({ width: '200px', 'background-color': 'yellow' }))]); const ngPlayer = factory.create(ngBox); ngPlayer.play(); }, 1000); }); } }
效果

动画效果是一模一样的。
Commonalities and Differences
我们看看它们的共同点和不同之处:
-
Angular Animation use many small functions
const factory = builder.build([animate('1s ease', style({ width: '200px', 'background-color': 'yellow' }))]); const ngPlayer = factory.create(ngBox);Angular Animation 需要调用许多小方法:build, animate, style, create,每一个传入一部分的 config。
反观 Web Animation API 就只调用一个 animation 方法,传入一个 config。
-
timings shorthand
Angular Animation 支持用 string 表达 timings,比如 ‘1s ease’ 相等于 Web Aniamtion API 的 { duration: 1000, easings: 'ease' }。
-
fill: 'forwards'
Angular Animation 不支持所有的 Web Animation API config,比如 fill 一定是 'forwards',这个我们不能改。
-
kebeb-case
Angular 支持 camelCase 也支持 kebab-case,像 CSS 那样 'background-color' 是可以接受的,
反观 Web Animation API 支支持 JavaScript 的 camelCase,不支持 CSS 的 kebab-case。
-
no auto play
Angular Animation 默认不会启动 animation,我们需要调用 play 方法让它启动。
ngPlayer.play();
反观 Web Animation API 默认会自动启动 animation,如果我们不希望这个行为,则需要调用 pause 方法将它暂停。
从上面几点,大家应该可以感受到,它俩的区别还是挺多的。所以我建议大家不要用 Web Animation API 的思路去揣摩 Angular Animation 的接口设计。
虽然 Angular Animation 底层是 Web Animation API,但它上层做了很多改动,接着往下看会看到更多不同之处。
AnimationPlayer
Web Animation API 调用 HTMLElement.animation 返回的是 player 类型是 Animation。
Angular Animation 调用 AnimationFactory.create 返回的 player 类型是 AnimationPlayer。
它俩不是继承关系,甚至可以说没什么关系😅。
AnimationPlayer 有自己的一套方法,我们一个一个看:
play, pause
ngPlayer.play();
ngPlayer.pause()
play 是启动 animation,pause 是暂停。
这和 Web Animation API 是一样的。
reset
reset 相等于 Web Animation API 的 finish。
restart
restart 等价于 reset + play。
finish
reset 相等于 Web Animation API 的 cancel。
reverse
不好意思,AnimationPlayer 没有 reverse 功能,Angular Animation 要实现 reverse 需要使用一些上层手法,下面会教。
onStart
ngPlayer.onStart(() => console.log('started'));
在 AnimationPlayer.play 之后触发。
onDone
onDone 监听的是 Web Animation API 的 finish event。
beforeDestroy, destroy, onDestroy
reset 表示重来,之后还会 play。
destroy 是完全销毁,之后不会再 play 了才使用。
在 destroy 之前最好先调用一下 beforeDestroy 方法。
onDestroy, onDone 都会在 destroy 后触发。
这里有个知识点:
reset 会调用 Web Animation API 的 cancel,但 Angular Animation 没有监听 cancel event。
reset 也不会触发 onDestroy,所以我们是没有方法可以监听到 reset 的。
init
在 AnimationFactory.create 返回 AnimationPlayer 以后,
我们看似已经有了 player,但其实内部的 Web Animation API player 是还没有创建的。
我们可以调用 init 方法创建它,或者之后调用 play 或其它方法,这些方法大部分会先判断是否已经 initialized,如果没有就会调用 init 方法。
相关代码在 web_animations_player.ts

注:domPlayer 就是 HTMLElement.animate 创建出来的原生 player,Angular Animation 没有公开这个对象,我们无法直接操作它。
getPosition, setPosition
getPosition 是 Web Animation API currentTime 的变种。

比如说,duration 是 10 秒,当前 currentTime 是 2 秒,那么 2 / 10 = 0.2。
所以 getPosition 返回的是一个百分比,1 代表完成,0.x 代表完成了多少八仙。
setPosition 也是一样的概念,duration 10 秒,setPosintion(0.5) 等价于 currentTime = 5000。
提醒:别忘了 delay 也要算进去哦,getPosition = currentTime / (duration + delay)。
总结
就目前介绍到的功能来看,Angular Animation 没有什么特别大的亮点,用 Web Animation API 还更灵活。
但好戏在后头嘛,下一 part 开始就会慢慢看到 Angular Animation 的各大强项了,继续吧🚀。
keyframes
Angular Animation 自然也可以设置 keyframes,但逻辑和 Web Animation API 有微微的不一样。
const nativePlayer = nativeBox.animate( [ { width: '200px', offset: 0.5 }, { width: '300px', backgroundColor: 'yellow', offset: 1 }, ], { duration: 3000, fill: 'forwards', easing: 'linear', }, ); const factory = builder.build([ animate( '3s linear', // 1. 在 animate 里面使用 keyframes 把多个 style 包起来, 每个 style 里 set offset keyframes([ style({ width: '200px', offset: 0.5 }), style({ width: '300px', 'background-color': 'yellow', offset: 1 }), ]), ), ]);
调用的方式还算挺直观的,多使用了一个 keyframes 函数。
效果

Web Animation API 的 background-color 比较早开始转变成黄色。
Web Animation API 对这个 command
[ { width: '200px', offset: 0.5 }, { width: '300px', backgroundColor: 'yellow', offset: 1 }, ]
的理解是,从 0 到 1 渐变成黄色。
而 Angular Animation 对这个 command 的理解是,从 0 到 0.5 和 background-color 无关,从 0.5 到 1 渐变成黄色,所以它俩的效果不一致。
谁比较合理,我也说不上来,反正 Angular Animation 的行为就是这样。
官网的声明:

group
接下来要介绍都是 Angular Animation 对 Web Animation API 的扩展功能,Web Animation API 没有 built-in 这些。
const factory = builder.build([ // 1. 传入 2 个 animate animate('1.5s ease', style({ width: '200px', 'background-color': 'yellow' })), animate('1s ease', style({ transform: 'rotate(360deg)' })), ]); const ngPlayer = factory.create(ngBox); ngPlayer.play();
同时传入 2 个 animate 效果是怎样的呢?

先执行第一个 aniamtion,等第一个结束后再执行第二个 aniamtion。
目录
上一篇 Angular 17+ 高级教程 – HttpClient
下一篇 Angular 17+ 高级教程 – Reactive Forms
想查看目录,请移步 Angular 17+ 高级教程 – 目录