Angular CSS 原生动画实战指南:@keyframes、过渡与 enter/leave 动画

发布时间:2026/9/7 15:25:03
Angular CSS 原生动画实战指南:@keyframes、过渡与 enter/leave 动画 Angular CSS 原生动画实战指南keyframes、过渡与 enter/leave 动画【免费下载链接】angularDeliver web apps with confidence 项目地址: https://gitcode.com/GitHub_Trending/an/angular本文基于 Angular 官方文档站指南 Using CSS to animate your application系统讲解在 Angular 应用中用纯 CSS而非 Angular 动画包实现完整动画体验的全部技术路径从keyframes定义可复用动画、类切换触发过渡、starting-styles与animate.enter/animate.leave处理进出场到交错stagger、并行组合、排序动画等复杂序列以及通过 Web Animations API 对动画进行编程式控制。读完本文你可以不依赖任何动画库仅用原生 CSS 少量 TypeScript 就覆盖日常开发中的绝大多数动画场景。一、为什么选择原生 CSS 动画CSS 提供了一套完整且强大的动画工具可以直接在 Angular 应用组件样式中创建丰富、流畅的动效。如果你尚未写过原生 CSS 动画可以先熟悉 MDN 的 CSS Animations 指南、W3Schools 的 CSS3 Animations 教程以及 Net Ninja 的 CSS Animation 系列视频然后再回到本指南。Angular 官方文档站为此指南配套了一个可交互示例应用所有示例组件都位于 adev/src/content/examples/animations/src/app/native-css 目录每篇文章中的代码块均可在该目录找到原始文件。二、创建可复用的 keyframes 动画通过keyframes可以把动画定义放在一个共享 CSS 文件中让应用各处复用同一组关键帧。官方示例定义了一个名为sharedAnimation的关键帧并用.animated-class类将它绑定到元素上——只要把该 class 加到某个元素上动画就会触发。来自 animations.csskeyframes sharedAnimation { to { height: 0; opacity: 1; background-color: red; } } .animated-class { animation: sharedAnimation 1s; }使用方式很简单在模板中通过类绑定或:host、if等条件渲染让元素获得animated-class类即触发 1 秒的sharedAnimation关键帧动画。关键帧本身与具体组件解耦是跨组件复用最自然的手段。三、用 CSS 类动画化状态切换很多场景需要动画化两个状态之间的变化例如元素的“打开/关闭”。做法是为每个状态定义一个 class并在其中用transition描述属性如何从当前值平滑过渡到目标值。来自 animations.css.open { height: 200px; opacity: 1; background-color: yellow; transition: all 1s; } .closed { height: 100px; opacity: 0.8; background-color: blue; transition: all 1s; }open/closed两个状态的触发是在组件模板中切换元素上的 CSS 类。类绑定的具体写法[class.xxx]expression与直接动画化行内样式的写法可参考 模板绑定指南 中的 CSS 类/样式属性绑定章节。3.1 过渡、时长与缓动函数调整时序timing、延迟delay与缓动easing几乎出现在每个动画中。CSS 提供了两种等价的方式分别指定独立属性或使用简写属性。对关键帧动画使用animation-duration、animation-delay、animation-timing-function或animation简写对不使用keyframes的过渡动画则使用transition-duration、transition-delay、transition-timing-function与transition简写。来自 animations.css/* 关键帧动画的时序控制 */ .example-element { animation-duration: 1s; animation-delay: 500ms; animation-timing-function: ease-in-out; } .example-shorthand { animation: exampleAnimation 1s ease-in-out 500ms; } /* 过渡动画的时序控制 */ .example-element { transition-duration: 1s; transition-delay: 500ms; transition-timing-function: ease-in-out; transition-property: margin-right; } .example-shorthand { transition: margin-right 1s ease-in-out 500ms; }两个简写的取值顺序都是名称/属性 时长 缓动 延迟。注意transition-property可以精确指定参与过渡的属性避免transition: all带来的不可预期行为下面的开/合示例即采用了transition-property的精确写法。3.2 完整示例类切换触发开/合动画动画的触发机制是当某个类出现在元素上时动画发生移除该类后元素回到该元素其他 CSS 规则定义的样子。下面这组官方示例完整展示了信号signal驱动的类切换 CSS 过渡的组合。open-close.tsimport {Component, signal} from angular/core; Component({ selector: app-open-close, templateUrl: open-close.html, styleUrls: [open-close.css], }) export class OpenClose { isOpen signal(true); toggle() { this.isOpen.update((isOpen) !isOpen); } }open-close.htmlh2Open / Close Example/h2 button typebutton classtoggle-btn (click)toggle()Toggle Open/Close/button div classopen-close-container [class.open]isOpen() pThe box is now {{ isOpen() ? Open : Closed }}!/p /divopen-close.css:host { display: block; margin-top: 1rem; } .open-close-container { border: 1px solid #dddddd; margin-top: 1em; padding: 20px 20px 0px 20px; font-weight: bold; font-size: 20px; height: 100px; opacity: 0.8; background: #3b82f6; color: #ebebeb; transition-property: height, opacity, background-color, color; transition-duration: 1s; } .toggle-btn { background: transparent; border: 1px solid var(--primary-contrast, black); color: var(--primary-contrast, black); padding: 10px 24px; border-radius: 8px; cursor: pointer; } .open { transition-duration: 0.5s; height: 200px; opacity: 1; background: #475569; color: #f9fafb; }要点解读默认样式收起态定义了height: 100px与transition-property: height, opacity, background-color, color因此任何向.open态或从.open态返回的属性变化都会走 1 秒过渡.open类覆盖了目标状态值height: 200px等并把transition-duration单独调为0.5s实现了“打开快、收起慢”的非对称节奏——这是纯 CSS 状态机的典型技巧不同状态各自拥有不同的过渡参数模板中[class.open]isOpen()把布尔信号与类绑定isOpen.update()完成状态翻转。四、过渡与触发器4.1 用 CSS Grid 动画化 auto 高度“从固定高度过渡到内容自适应高度auto”是经典难题因为height: auto无法直接插值。纯 CSS 的优雅解法是 CSS Grid 的fr行高技巧把容器设为单列 Grid在0fr与1fr之间过渡子元素借助overflow: hidden裁切溢出即可实现视觉上的高度自动展开/收起。auto-height.ts 与 auto-height.html 沿用上文的信号 [class.open]模式button typebutton classtoggle-btn (click)toggle()Toggle Open/Close/button div classcontainer [class.open]isOpen() div classcontent pThe box is now {{ isOpen() ? Open : Closed }}!/p /div /divauto-height.css.container { display: grid; grid-template-rows: 0fr; overflow: hidden; transition: grid-template-rows 1s; } .container.open { grid-template-rows: 1fr; } .container .content { min-height: 0; transition: visibility 1s; padding: 0 20px; visibility: hidden; margin-top: 1em; font-weight: bold; font-size: 20px; background: #3b82f6; color: #ebebeb; overflow: hidden; } .container.open .content { visibility: visible; }关键细节grid-template-rows: 0fr → 1fr的过渡是核心——fr单位参与插值0fr即“占 0 高度”1fr即“占满内容所需高度”子元素.content必须overflow: hidden且通过min-height: 0解除 Grid 子项的默认最小高度约束否则折叠时内容会撑破容器visibility同步用 1 秒过渡让收起时内容淡出的时间点与高度变化一致。如果你的浏览器支持范围足够新还可以直接使用calc-size()它是动画化 auto 尺寸的“真正解法”可查阅 MDN 的calc-size()文档了解用法。4.2 元素进入视图的动画animate.enter当元素因条件渲染“进入”DOM 时可以用 Angular 的animate.enter指令它在元素进入视图的那一刻应用指定的动画类。insert.htmlnav button typebutton classtoggle-btn (click)toggle()Toggle Element/button /nav if (isShown()) { div classinsert-container animate.enterenter-animation pThe box is inserted/p /div }insert.css.insert-container { border: 1px solid #dddddd; margin-top: 1em; padding: 20px; font-weight: bold; font-size: 20px; } .enter-animation { animation: slide-fade 1s; } keyframes slide-fade { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }insert.ts 中的isShown signal(false)配合if控制插入/移除当if分支首次渲染、div进入 DOM 时animate.enter添加enter-animation类slide-fade关键帧播放一次从下方 20px、透明滑入到原位。4.3 元素离开视图的动画animate.leave离场动画与入场对称用animate.leave指定元素离开视图时应用的 CSS 类。官方示例里容器本身还用starting-style定义了自己的入场初始值透明而离场则交给.deleting类的过渡完成。remove.htmlif (isShown()) { div classinsert-container animate.leavedeleting pThe box is inserted/p /div }remove.css.insert-container { border: 1px solid #dddddd; margin-top: 1em; padding: 20px; font-weight: bold; font-size: 20px; opacity: 1; transition: opacity 200ms ease-in; starting-style { opacity: 0; } } .deleting { opacity: 0; transform: translateY(20px); transition: opacity 500ms ease-out, transform 500ms ease-out; }工作流元素插入时由starting-style声明初始opacity: 0随后过渡到opacity: 1200ms 淡入当isShown变为false、元素即将被移除时animate.leave先加上deleting类执行 500ms 的淡出 下移动画结束后元素才被真正移出 DOM——这保证了“先播完离场动画再删除”的语义。注意子组件的animate.leave动画只在同一组件模板内触发。嵌套组件的animate.leave动画不会因为父元素被移除而触发。更多细节见 Enter and Leave animations 指南。4.4 数字增减动画increment / decrement数字递增/递减是应用中的常见模式数值变化时让文本快速放大变色再回弹。这个示例展示了“手动加类 监听animationend事件移除类”的完整生命周期管理。increment-decrement.tsimport {Component, ElementRef, OnInit, signal, viewChild} from angular/core; Component({ selector: app-increment-decrement, templateUrl: increment-decrement.html, styleUrls: [increment-decrement.css], }) export class IncrementDecrement implements OnInit { num signal(0); el viewChildElementRefHTMLParagraphElement(el); ngOnInit() { this.el()?.nativeElement.addEventListener(animationend, (ev) { if (ev.animationName.endsWith(decrement) || ev.animationName.endsWith(increment)) { this.animationFinished(); } }); } modify(n: number) { const targetClass n 0 ? increment : decrement; this.num.update((v) (v n)); this.el()?.nativeElement.classList.add(targetClass); } animationFinished() { this.el()?.nativeElement.classList.remove(increment, decrement); } ngOnDestroy() { this.el()?.nativeElement.removeEventListener(animationend, this.animationFinished); } }increment-decrement.htmlh3Increment and Decrement Example/h3 section p #elNumber {{ num() }}/p div classcontrols button typebutton (click)modify(1)/button button typebutton (click)modify(-1)-/button /div /sectionincrement-decrement.css 中定义了两组关键帧.increment { animation: increment 300ms; } .decrement { animation: decrement 300ms; } keyframes increment { 33% { color: green; transform: scale(1.3, 1.2); } 66% { color: green; transform: scale(1.2, 1.2); } 100% { transform: scale(1, 1); } } keyframes decrement { 33% { color: red; transform: scale(0.8, 0.9); } 66% { color: red; transform: scale(0.9, 0.9); } 100% { transform: scale(1, 1); } }这条示例完整体现了纯 CSS 动画在 Angular 中的典型工程模式模板引用#elviewChild拿到 DOM 元素句柄状态变化时手动classList.add(increment | decrement)监听animationend事件按animationName校验后移除类——类移除后关键帧动画即结束为下一次触发做好准备关键帧用 33% / 66% / 100% 三段做“放大—回落”的弹性节奏。4.5 禁用动画的三种方式当需要让某些动画不播放时文档给出了三条路径方式一强制置空的自定义类。定义一个把animation和transition都设为none的类应用到元素上即可阻止该元素上任何动画.no-animation { animation: none !important; transition: none !important; }你可以把它作用在单个元素上也可以作用到整个 DOM 或 DOM 的某个区段以全局强制。但要注意这会连带阻止动画事件的触发。如果你的逻辑依赖等待动画事件来完成元素移除例如离场动画结束后才删除节点这种方案会失效——文档给出的替代做法是把动画时长设为 1 毫秒既接近瞬时又保留事件语义。方式二prefers-reduced-motion媒体查询。利用系统级的prefers-reduced-motion媒体查询MDN 有专页说明该查询在用户系统偏好“减少动态效果”时自动关闭所有动画这是无障碍a11y层面的标准做法。方式三程序化地阻止添加动画类。在组件逻辑里直接控制不添加触发动画的类动画自然不播放。这是最可控、也最容易与业务状态联动的方式。4.6 动画回调事件如果需要在动画生命周期的特定时点执行逻辑DOM 提供了完整的事件监听面关键帧动画事件animationstart、animationend、animationiteration每次循环迭代、animationcancel过渡事件transitionstart、transitionrun、transitionend、transitioncancel。Web Animations API 还暴露了远超 CSS 声明式能力的编程接口可查阅 MDN 的 Web Animations API 文档了解全貌。注意事件冒泡bubbling当父、子元素都在做动画时子元素的事件会冒泡到父元素上。此时应考虑stopPropagation()或检查事件对象中的animationName、正在过渡的属性等信息确认自己响应的是目标节点的事件而不是从子节点冒泡上来的事件。五、复杂动画序列真实应用的动画往往不止一个淡入淡出而是多个动画组合编排。5.1 列表项交错stagger动画让列表中每一项依次延迟入场形成级联效果核心是animation-delay/transition-delay配合每项的索引。官方示例把索引写入 CSS 自定义属性--index用calc()计算逐项延迟stagger.htmlh1Stagger Example/1 button typebutton classtoggle-btn (click)refresh()Refresh/button div classitems-container if (show()) { ul classitems for (item of items; track $index) { li classitem style--index: {{ $index }}{{ item }}/li } /ul } /divstagger.css.items .item { transition-property: opacity, transform; transition-duration: 500ms; transition-delay: calc(200ms * var(--index)); starting-style { opacity: 0; transform: translateX(-10px); } }stagger.ts 的refresh()先把show置为false10ms 后再置回true通过“移除再插入”重新触发starting-style驱动的入场过渡。拆解要点每个li通过内联样式携带--index0、1、2……transition-delay: calc(200ms * var(--index))使第 n 项晚 200ms×n 开始过渡天然形成瀑布节奏starting-style声明了元素首次出现在文档中时的起始样式透明 左移 10px之后过渡到默认样式实现无需keyframes的入场动画外层if (show())的整体插拔是重播手段——这也说明for项在“移除并重新加入”时会重新触发入场动画见下文排序示例。5.2 并行动画Parallel Animationsanimation简写支持在一条声明里列出多个动画每个动画可拥有独立的时长与延迟从而在一个元素上并行组合多个效果.target-element { animation: rotate 3s, fade-in 2s; }在这个例子中rotate3s与fade-in2s同时开始、同时结束各自的生命周期互不干扰。这是纯 CSS 下“组合出复杂效果”最轻量的手段。5.3 列表重排序动画reorderfor循环中的项目在数据顺序变化时会被移除并重新加入这会触发入场动画——配合starting-styles即starting-style块即可获得“换位/重排时重新淡入”的效果也可以改用animate.enter达到同样的行为。对于被移除的项目用animate.leave播放离场动画reorder.htmlh1Reordering List Example/1 button typebutton classtoggle-btn (click)randomize()Randomize/button ul classitems for (item of items; track item) { li classitem animate.leavefade{{ item }}/li } /ulreorder.css.items .item { transition-property: opacity, transform; transition-duration: 500ms; starting-style { opacity: 0; transform: translateX(-10px); } } .items .item.fade { animation: fade-out 500ms; } keyframes fade-out { from { opacity: 1; } to { opacity: 0; } }reorder.ts 的randomize()通过 Fisher-Yates 风格的洗牌重排items数组模板以track item追踪项身份身份不变的项保持 DOM 节点仅顺序改变而被判定移除的项在离开前由animate.leavefade播放fade-out关键帧。这样重排既有序变化又有离场/重入的视觉反馈。六、动画的编程式控制除了声明式地“加类即播放”还可以直接从元素上取回正在运行的动画对象调用 DOM 的Element.getAnimations()它会返回该元素上所有Animation实例的数组。拿到Animation对象后Web Animations API 提供的能力远超旧版动画包的AnimationPlayer——你可以对每个动画cancel()、play()、pause()、reverse()乃至操作currentTime、playbackRate等属性。对绝大多数“控制动画”的需求这套原生 API 已经足够。七、延伸阅读如果你想继续深入 Angular 的动画体系可以阅读文档站中的以下指南Enter and Leave animations进入与离开动画 ——animate.enter/animate.leave的完整语义与边界Route transition animations路由过渡动画 —— 路由切换场景的动画方案动画指南总入口guide/animations。小结场景核心技术参考实现跨组件复用keyframes 共享 CSS 类animations.css状态切换[class.x]transitionopen-close.cssauto 高度Grid0fr → 1fr过渡auto-height.css入场/离场animate.enter/animate.leavestarting-styleinsert.css、remove.css数字增减手动加类 animationend移除类increment-decrement.ts级联/交错transition-delay: calc(200ms * var(--index))stagger.css列表重排for移除重插 starting-styleanimate.leavereorder.ts编程控制Element.getAnimations() Web Animations APIDOM 标准 API关闭动画.no-animation类 /prefers-reduced-motion/ 不添加类见“禁用动画”一节纯 CSS 方案的优势在于零依赖、性能好合成层属性transform/opacity可走 GPU 加速、且与 Angular 信号驱动的响应式模型天然契合组件只负责翻转信号与类CSS 负责一切视觉插值。掌握本文的类切换、starting-style、animate.enter/animate.leave与事件回调四条主线即可覆盖 Angular 应用中绝大多数动画需求。【免费下载链接】angularDeliver web apps with confidence 项目地址: https://gitcode.com/GitHub_Trending/an/angular创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考