Skip to main content

页面动画(Angular Animation)

本节介绍如何在本框架中使用 Angular 动画 提升页面交互体验,包括动画原理、实现步骤,以及从零到可运行的完整配置与示例。

1. 动画原理简述​

Angular 的 @angular/animations 基于「状态 + 过渡」模型:

  • Trigger(触发器):给动画起一个名字,并绑定到组件模板的某个元素上,由组件状态驱动何时执行。
  • State(状态):描述元素在某一时刻的样式(如 open / closed),用 state() 和 style() 定义。
  • Transition(过渡):描述从一个状态到另一个状态时如何变化,用 transition() 和 animate() 指定时长与缓动。

因此实现动画的通用步骤是:

  1. 在应用中启用动画(provideAnimations() 或 provideAnimationsAsync())。
  2. 在组件的 animations 数组中定义 trigger,内部用 state 和 transition 描述各状态及过渡。
  3. 在模板中通过 [@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,并保持「状态 + 过渡」的同一套思路。