Angular 可复用动画:用 animation() 与 useAnimation() 打造可共享的动画定义
本篇聚焦 Angular 官方文档中"可复用动画(Reusable animations)"这一主题:讲解如何用 @angular/animations 提供的 animation() 函数把一段动画定义抽取到独立 .ts 文件中并导出为常量,再通过 useAnimation() 在多个组件中复用。读完本文,你将掌握如何组织可复用动画的模块结构、如何替换运行时参数(params)、如何在 @Component 中挂载 trigger 使其生效,并能结合仓库源码理解 animation() / useAnimation() 的底层元数据结构。
重要前提:
@angular/animations包在当前仓库中已被标记为弃用(deprecated)。官方团队建议新代码使用原生 CSS 配合animate.enter与animate.leave来实现动画。若你正在维护既有动画代码,本文的可复用写法仍然适用;若从零开始,建议先阅读迁移指南与enter/leave 动画概览。
为什么需要"可复用"的动画
Angular 的动画系统构建在 CSS 能力之上,可以动画化浏览器认为可动画化的任意属性(位置、尺寸、transform、颜色、边框等)。在实际应用中,同一段"过渡动画"往往需要被多个组件共享,例如统一的开合(open/close)效果、列表项的高度变化等。如果每次都把动画写死在每个组件里,会造成大量重复代码且难以统一维护。
@angular/animations 提供了两个专门用于"复用"的函数:
| 函数 | 作用 |
|---|---|
animation() |
生成一段可复用的动画定义(返回 AnimationReferenceMetadata),供别处通过 useAnimation() 调用。 |
useAnimation() |
激活一段可复用动画(返回 AnimationAnimateRefMetadata),与 animation() 配合使用。 |
这套"定义一次、处处引用"的模式,就是本文要展开的核心。
第一步:用 animation() 定义并导出可复用动画
要创建可复用动画,做法是用 animation() 函数在独立的 .ts 文件中定义动画,并把这段动画定义声明为一个导出的 const 变量。仓库中的示例文件 animations.1.ts 展示了这一点:
// animations.1.ts
import {
animation,
style,
animate,
trigger,
transition,
useAnimation,
} from '@angular/animations';
export const transitionAnimation = animation([
style({
height: '{{ height }}',
opacity: '{{ opacity }}',
backgroundColor: '{{ backgroundColor }}',
}),
animate('{{ time }}'),
]);
这里有两个关键点值得注意:
transitionAnimation通过export变成一个可被其他模块引用的常量,这就是"可复用"的来源。{{ height }}、{{ opacity }}、{{ backgroundColor }}、{{ time }}这些{{ ... }}占位符会在运行时被替换。换句话说,动画定义本身不写死具体数值,而是留出一组"插槽",由调用方在激活时填充。
不依赖参数的可复用动画
如果你希望这段动画是"固定样式"、不需要调用方传参,可以直接把具体值写进定义。同一示例文件中还给出了一个不含占位符的版本:
export const sharedAnimation = animation([
style({
height: 0,
opacity: 1,
backgroundColor: 'red',
}),
animate('1s'),
]);
可以看到,animation() 的入参就是普通动画步骤数组(这里由 style() 与 animate() 组成)。它既可以承载带占位符的"参数化"动画,也可以承载完全固定的动画,二者只是取值方式不同。
第二步:用 trigger() 导出"动画触发器"
除了导出"动画步骤",你还可以把整段 trigger(含状态与过渡)也作为常量导出,方便多处直接引用。示例文件中接着给出了一个触发器定义:
// animations.1.ts
export const triggerAnimation = trigger('openClose', [
transition('open => closed', [
useAnimation(transitionAnimation, {
params: {
height: 0,
opacity: 1,
backgroundColor: 'red',
time: '1s',
},
}),
]),
]);
这里体现了完整的"参数替换"流程:
transition('open => closed', [...])声明了从open状态到closed状态的单向过渡;useAnimation(transitionAnimation, { params: {...} })激活第一步导出的可复用动画,并在params对象中为每个占位符提供具体取值——height: 0、opacity: 1、backgroundColor: 'red'、time: '1s';- 这些
params的键名必须与定义里的{{ ... }}占位符一一对应。
这样,triggerAnimation 本身就是一个自包含、可整体复用的动画单元。
第三步:在组件中导入并复用
定义好可复用变量后,即可把它们导入组件类并使用。示例组件 open-close.3.ts 展示了导入 transitionAnimation 并通过 useAnimation() 挂载到组件的过程:
// open-close.3.ts
import { Component, input } from '@angular/core';
import { transition, trigger, useAnimation, AnimationEvent } from '@angular/animations';
import { transitionAnimation } from './animations';
@Component({
selector: 'app-open-close-reusable',
animations: [
trigger('openClose', [
transition('open => closed', [
useAnimation(transitionAnimation, {
params: {
height: 0,
opacity: 1,
backgroundColor: 'red',
time: '1s',
},
}),
]),
]),
],
templateUrl: 'open-close.html',
styleUrls: ['open-close.css'],
})
export class OpenCloseBooleanComponent {
isOpen = false;
toggle() {
this.isOpen = !this.isOpen;
}
logging = input(false);
onAnimationEvent(event: AnimationEvent) {
if (!this.logging()) {
return;
}
}
}
这段代码的复用要点在于:
animations:元数据属性位于@Component()装饰器内部,用于声明组件用到的触发器。正如概览文档所述,动画定义写在"控制被动画 HTML 元素的组件"的元数据里。trigger('openClose', [...])给动画起了唯一名字openClose,随后即可在模板里用[@openClose]="expression"的形式把触发器绑定到具体元素上;当表达式值变化到某个已定义状态时,动画被触发执行。useAnimation(transitionAnimation, { params })是复用落地的关键:组件并没有重新书写动画步骤,而是"引用"了外部导出的常量,并传入本次使用的具体参数。
对比 open-close.3.ts 与 animations.1.ts 可以清楚看到:组件只需一行 import { transitionAnimation } from './animations',就能把定义复用到任意数量的组件中,这正是可复用动画相对"内联写法"的核心优势。
源码视角:animation() 与 useAnimation() 的底层结构
为了印证上文"占位符在运行时被替换、参数经 params 传入"的机制,可以直接查看 @angular/animations 包的源码实现。在 animation_metadata.ts 中,这两个函数分别位于第 1154 行与第 1200 行附近,其本质是生成两类动画元数据对象:
// packages/animations/src/animation_metadata.ts (约 L1154)
export function animation(
steps: AnimationMetadata | AnimationMetadata[],
options: AnimationOptions | null = null,
): AnimationReferenceMetadata {
return { type: AnimationMetadataType.Reference, animation: steps, options };
}
// packages/animations/src/animation_metadata.ts (约 L1200)
export function useAnimation(
animation: AnimationReferenceMetadata,
options: AnimationOptions | null = null,
): AnimationAnimateRefMetadata {
return { type: AnimationMetadataType.AnimateRef, animation, options };
}
从源码结构看,可以得出几点实现层面的事实:
animation()的返回值类型为AnimationReferenceMetadata,它把"步骤数组 + 选项"打包成一个带Reference类型标记的对象,代表一段"可被引用的动画"。useAnimation()的返回值类型为AnimationAnimateRefMetadata,其内部持有对前者的引用(animation字段)以及自己的options(即运行时params)。AnimationOptions允许开发者提供"额外覆盖值",这正是{{ ... }}占位符被替换的入口。- 因此"定义—激活"两步走,本质上是先生成一个可引用对象,再在激活点带上参数把它实例化进具体的
transition序列中。这也解释了为什么占位符必须与params的键一一对应——参数替换发生在激活环节而非定义环节。
小结与延伸阅读
本文沿着官方文档"Reusable animations"的脉络,完整走通了可复用动画的三段式流程:用 animation() 定义并导出常量、用 useAnimation() 激活并填充 params、在组件的 animations: 元数据中通过 trigger() 挂载。配合 animation_metadata.ts 的源码,可以进一步确认这两个函数在元数据层面的引用关系,以及运行时参数替换的落点。
需要再次强调:@angular/animations 在当前仓库中已被标记弃用,官方推荐新代码使用原生 CSS 与 animate.enter / animate.leave。相关深入内容可参考仓库内的:
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00