页面动画(Angular Animation)
本节介绍如何在本框架中使用 Angular 动画 提升页面交互体验,包括动画原理、实现步骤,以及从零到可运行的完整配置与示例。
1. 动画原理简述
Angular 的 @angular/animations 基于「状态 + 过渡」模型:
- Trigger(触发器):给动画起一个名字,并绑定到组件模板的某个元素上,由组件状态驱动何时执行。
- State(状态):描述元素在某一时刻的样式(如
open/closed),用state()和style()定义。 - Transition(过渡):描述从一个状态到另一个状态时如何变化,用
transition()和animate()指定时长与缓动。
因此实现动画的通用步骤是:
- 在应用中启用动画(
provideAnimations()或provideAnimationsAsync())。 - 在组件的
animations数组中定义trigger,内部用state和transition描述各状态及过渡。 - 在模板中通过
[@triggerName]="stateExpression"将触发器绑定到元素,状态变化时自动播放过渡。
下面按「启用 → 定义 → 绑定」的顺序给出完整示例。
2. 启用动画
在应用配置中提供动画能力(与 getting-started 中一致,若已配置可跳过):
// app.config.ts 或 main.ts 中的 ApplicationConfig
import { provideAnimations } from '@angular/platform-browser/animations';
// 若希望懒加载动画模块以减小初始包体积,可使用:
// import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
export const appConfig: ApplicationConfig = {
providers: [
provideAnimations(), // 或 provideAnimationsAsync()
// ... 其他 providers
],
};
3. 完整示例:手把手编写与配置
下面以一个「可展开/收起的面板」为例,从组件到模板完整写一遍。
3.1 定义状态与过渡
在组件中引入动画 API,并在 @Component 的 animations 里定义 trigger、state、transition:
// expandable-panel.component.ts
import { Component } from '@angular/core';
import {
trigger,
state,
style,
transition,
animate,
} from '@angular/animations';
@Component({
selector: 'app-expandable-panel',
standalone: true,
template: `
<div class="panel-header" (click)="toggle()">
{{ title }}
<span class="icon">{{ isOpen ? '▼' : '▶' }}</span>
</div>
<div [@expandCollapse]="isOpen ? 'open' : 'closed'" class="panel-body">
<ng-content></ng-content>
</div>
`,
styles: [`
.panel-header { cursor: pointer; padding: 8px 12px; background: #f5f5f5; }
.panel-body { overflow: hidden; }
`],
animations: [
trigger('expandCollapse', [
state('closed', style({
height: '0',
opacity: 0,
overflow: 'hidden',
})),
state('open', style({
height: '*',
opacity: 1,
overflow: 'hidden',
})),
transition('closed => open', animate('300ms ease-out')),
transition('open => closed', animate('200ms ease-in')),
]),
],
})
export class ExpandablePanelComponent {
isOpen = false;
title = '点击展开/收起';
toggle(): void {
this.isOpen = !this.isOpen;
}
}
要点说明:
state('closed', style({ ... })):收起时的最终样式;state('open', style({ ... })):展开时的最终样式。height: '*'表示由浏览器根据内容计算高度;展开前先用height: '0'才能形成「从 0 到内容高度」的动画。transition('closed => open', animate('300ms ease-out'))表示从closed到open的过渡时长与缓动。
3.2 模板中绑定触发器
模板里用 [@expandCollapse]="isOpen ? 'open' : 'closed'" 把触发器绑到包裹内容的 div 上,这样 isOpen 变化时就会自动执行对应 transition。
3.3 可选:入场/离场动画(*ngIf + :enter / :leave)
列表项或弹层经常需要「进入/离开」动画,可配合 *ngIf 使用 :enter 和 :leave 过渡:
// list-with-animations.component.ts(节选)
import { trigger, transition, style, animate } from '@angular/animations';
@Component({
// ...
animations: [
trigger('listItem', [
transition(':enter', [
style({ opacity: 0, transform: 'translateY(-8px)' }),
animate('200ms ease-out', style({ opacity: 1, transform: 'translateY(0)' })),
]),
transition(':leave', [
animate('150ms ease-in', style({ opacity: 0, transform: 'translateX(-12px)' })),
]),
]),
],
})
export class ListWithAnimationsComponent {
items = [1, 2, 3];
remove(i: number): void {
this.items.splice(i, 1);
}
}
<div *ngFor="let item of items; let i = index" [@listItem]>
Item {{ item }}
<button (click)="remove(i)">移除</button>
</div>
:enter:元素被插入 DOM 时(如*ngIf从 false 变 true,或*ngFor新增项)。:leave:元素从 DOM 移除前执行,动画结束后 Angular 再移除节点。
4. 实现过程小结
| 步骤 | 说明 |
|---|---|
| 1. 启用动画 | 在 ApplicationConfig 中 provideAnimations() 或 provideAnimationsAsync()。 |
| 2. 定义 trigger | 在 @Component.animations 里用 trigger(name, [ state(...), transition(...) ])。 |
| 3. 绑定到模板 | 在模板中用 [@triggerName]="stateExpression" 绑定到要动画的元素。 |
| 4. 驱动状态 | 通过组件属性(如 isOpen)或列表变化(*ngIf / *ngFor)驱动状态变化,触发过渡。 |
按上述四步即可在本框架内为任意页面或组件加上动画,并与 AbstractAppPage、事件总线等现有能力并存使用。若希望进一步做路由级过渡或更复杂的关键帧,可在此基础上使用 query()、group()、keyframes() 等 API,并保持「状态 + 过渡」的同一套思路。