設計良好的動畫能讓應用程式更直覺、更吸引人,但它不只是裝飾。
動畫能從多方面改善應用程式與使用者體驗:
沒有動畫時,網頁轉場容易顯得突兀生硬
動態效果能大幅提升使用者體驗,讓使用者察覺應用程式對其操作的回應
良好的動畫能在整個工作流程中順暢地引導使用者的注意力
Angular 提供 animate.enter 與 animate.leave 來為應用程式的元素加入動畫。這兩個功能會在適當的時機套用進入與離開的 CSS 類別,或呼叫函式以套用第三方函式庫的動畫。animate.enter 與 animate.leave 不是指令,而是由 Angular 編譯器直接支援的特殊 API。它們可以直接用在元素上,也可以作為宿主繫結使用。
你可以用 animate.enter 讓元素在進入 DOM 時播放動畫。你可以用 CSS 類別搭配轉場或關鍵影格動畫來定義進入動畫。
import { Component , signal } from '@angular/core' ;
@ Component ({
selector: 'app-enter' ,
templateUrl: 'enter.html' ,
styleUrls: [ 'enter.css' ],
})
export class Enter {
isShown = signal ( false );
toggle () {
this .isShown. update (( isShown ) => ! isShown);
}
}
< h2 >< code >animate.enter</ code > Example</ h2 >
< button type = "button" class = "toggle-btn" (click) = "toggle()" >Toggle Element</ button >
@if ( isShown ()) {
< div class = "enter-container" animate.enter = "enter-animation" >
< p >The box is entering.</ p >
</ div >
}
:host {
display : block ;
height : 200 px ;
}
.enter-container {
border : 1 px solid #dddddd ;
margin-top : 1 em ;
padding : 20 px ;
font-weight : bold ;
font-size : 20 px ;
}
.enter-container p {
margin : 0 ;
}
.enter-animation {
animation : slide-fade 1 s ;
}
@keyframes slide-fade {
from {
opacity : 0 ;
transform : translateY ( 20 px );
}
to {
opacity : 1 ;
transform : translateY ( 0 );
}
}
.toggle-btn {
background : transparent ;
border : 1 px solid var ( --primary-contrast , black );
color : var ( --primary-contrast , black );
padding : 10 px 24 px ;
border-radius : 8 px ;
cursor : pointer ;
}
動畫完成後,Angular 會從 DOM 移除你在 animate.enter 中指定的類別。動畫類別只會在動畫進行期間存在。
NOTE: 在同一個元素上使用多個關鍵影格動畫或轉場屬性時,Angular 會等到最長的那個動畫完成後,才移除所有類別。
你可以將 animate.enter 與任何其他 Angular 功能搭配使用,例如控制流程或動態運算式。animate.enter 接受單一類別字串(多個類別以空格分隔),或類別字串陣列。
順帶說明 CSS 轉場的用法:若你選擇使用轉場而非關鍵影格動畫,透過 animate.enter 加入元素的類別,代表的是轉場要動畫到的目標狀態。當沒有動畫執行時,元素基礎 CSS 的外觀,通常會與 CSS 轉場的結束狀態相似。因此,你仍需要搭配 @starting-style,讓轉場有適當的起始狀態才能正常運作。
import { Component , signal } from '@angular/core' ;
@ Component ({
selector: 'app-enter-binding' ,
templateUrl: 'enter-binding.html' ,
styleUrls: [ 'enter-binding.css' ],
})
export class EnterBinding {
isShown = signal ( false );
toggle () {
this .isShown. update (( isShown ) => ! isShown);
}
enterClass = signal ( 'enter-animation' );
}
< h2 >< code >animate.enter</ code > Binding Example</ h2 >
< button type = "button" class = "toggle-btn" (click) = "toggle()" >Toggle Element</ button >
@if ( isShown ()) {
< div class = "enter-container" [animate.enter] = "enterClass()" >
< p >The box is entering.</ p >
</ div >
}
:host {
display : block ;
height : 200 px ;
}
.enter-container {
border : 1 px solid #dddddd ;
margin-top : 1 em ;
padding : 20 px ;
font-weight : bold ;
font-size : 20 px ;
}
.enter-container p {
margin : 0 ;
}
.enter-animation {
animation : slide-fade 1 s ;
}
@keyframes slide-fade {
from {
opacity : 0 ;
transform : translateY ( 20 px );
}
to {
opacity : 1 ;
transform : translateY ( 0 );
}
}
.toggle-btn {
background : transparent ;
border : 1 px solid var ( --primary-contrast , black );
color : var ( --primary-contrast , black );
padding : 10 px 24 px ;
border-radius : 8 px ;
cursor : pointer ;
}
你可以用 animate.leave 讓元素在離開 DOM 時播放動畫。你可以用 CSS 類別搭配 transform 或關鍵影格動畫來定義離開動畫。
import { Component , signal } from '@angular/core' ;
@ Component ({
selector: 'app-leave' ,
templateUrl: 'leave.html' ,
styleUrls: [ 'leave.css' ],
})
export class Leave {
isShown = signal ( false );
toggle () {
this .isShown. update (( isShown ) => ! isShown);
}
}
< h2 >< code >animate.leave</ code > Example</ h2 >
< button type = "button" class = "toggle-btn" (click) = "toggle()" >Toggle Element</ button >
@if ( isShown ()) {
< div class = "leave-container" animate.leave = "leaving" >
< p >Goodbye</ p >
</ div >
}
:host {
display : block ;
height : 200 px ;
}
.leave-container {
border : 1 px solid #dddddd ;
margin-top : 1 em ;
padding : 20 px ;
font-weight : bold ;
font-size : 20 px ;
opacity : 1 ;
transition : opacity 200 ms ease-in ;
@starting-style {
opacity : 0 ;
}
}
.leave-container p {
margin : 0 ;
}
.leaving {
opacity : 0 ;
transform : translateY ( 20 px );
transition :
opacity 500 ms ease-out ,
transform 500 ms ease-out ;
}
.toggle-btn {
background : transparent ;
border : 1 px solid var ( --primary-contrast , black );
color : var ( --primary-contrast , black );
padding : 10 px 24 px ;
border-radius : 8 px ;
cursor : pointer ;
}
動畫完成後,Angular 會自動把該元素從 DOM 移除。
NOTE: 在同一個元素上使用多個關鍵影格動畫或轉場屬性時,Angular 會等到其中最長的那個動畫完成後,才移除元素。
animate.leave 也可以搭配訊號與其他繫結使用。你可以為 animate.leave 指定單一類別或多個類別,用以空格分隔的簡單字串或字串陣列指定皆可。
import { Component , signal } from '@angular/core' ;
@ Component ({
selector: 'app-leave-binding' ,
templateUrl: 'leave-binding.html' ,
styleUrls: [ 'leave-binding.css' ],
})
export class LeaveBinding {
isShown = signal ( false );
toggle () {
this .isShown. update (( isShown ) => ! isShown);
}
farewell = signal ( 'leaving' );
}
< h2 >< code >animate.leave</ code > Binding Example</ h2 >
< button type = "button" class = "toggle-btn" (click) = "toggle()" >Toggle Element</ button >
@if ( isShown ()) {
< div class = "leave-container" [animate.leave] = "farewell()" >
< p >Goodbye</ p >
</ div >
}
:host {
display : block ;
height : 200 px ;
}
.leave-container {
border : 1 px solid #dddddd ;
margin-top : 1 em ;
padding : 20 px ;
font-weight : bold ;
font-size : 20 px ;
opacity : 1 ;
transition : opacity 200 ms ease-in ;
@starting-style {
opacity : 0 ;
}
}
.leave-container p {
margin : 0 ;
}
.leaving {
opacity : 0 ;
transform : translateY ( 20 px );
transition :
opacity 500 ms ease-out ,
transform 500 ms ease-out ;
}
.toggle-btn {
background : transparent ;
border : 1 px solid var ( --primary-contrast , black );
color : var ( --primary-contrast , black );
padding : 10 px 24 px ;
border-radius : 8 px ;
cursor : pointer ;
}
animate.leave 動畫如何執行、何時發生,其中有些細微差別。當 animate.leave 放在正被移除的元素上時,它會生效;而若 animate.leave 放在被移除元素的後代 元素上、且位於同一個元件範本內,這些子元素的 animate.leave 動畫會在父節點從 DOM 移除之前 先執行。這能確保你可以放心地讓子元素以動畫離場,父節點不會提早消失。
import { Component , signal } from '@angular/core' ;
@ Component ({
selector: 'app-leave-parent' ,
templateUrl: 'leave-parent.html' ,
styleUrls: [ 'leave-parent.css' ],
})
export class LeaveParent {
isShown = signal ( false );
toggle () {
this .isShown. update (( isShown ) => ! isShown);
}
}
< h2 >< code >animate.leave</ code > Parent Sub-tree Animation Example</ h2 >
< button type = "button" class = "toggle-btn" (click) = "toggle()" >Toggle Element</ button >
@if ( isShown ()) {
< div class = "leave-parent" >
<!-- the child node with `animate.leave` will animate first
before the parent node is removed from the DOM. -->
< div class = "leave-container" animate.leave = "leaving" >
< p >Goodbye</ p >
</ div >
</ div >
}
:host {
display : block ;
height : 200 px ;
}
.leave-container {
border : 1 px solid #dddddd ;
margin-top : 1 em ;
padding : 20 px ;
font-weight : bold ;
font-size : 20 px ;
opacity : 1 ;
transition : opacity 200 ms ease-in ;
@starting-style {
opacity : 0 ;
}
}
.leave-container p {
margin : 0 ;
}
.leaving {
opacity : 0 ;
transform : translateY ( 20 px );
transition :
opacity 500 ms ease-out ,
transform 500 ms ease-out ;
}
.toggle-btn {
background : transparent ;
border : 1 px solid var ( --primary-contrast , black );
color : var ( --primary-contrast , black );
padding : 10 px 24 px ;
border-radius : 8 px ;
cursor : pointer ;
}
IMPORTANT: 子元素動畫只會對同一個元件範本內的元素觸發。若被移除的元素包含子元件,那些子元件範本內定義的任何 animate.leave 動畫,都不會 在父元素被移除前執行。若要在移除時為子元件播放動畫,請改在父範本內直接把 animate.leave 套用到子元件的宿主元素上,或以程式方式在子元件中觸發動畫,並延遲父元素的移除,直到該動畫完成。
animate.enter 與 animate.leave 都支援事件繫結語法,可以用來呼叫函式。你可以利用這個語法呼叫元件程式碼中的函式,或使用第三方動畫函式庫,例如 GSAP 、anime.js ,或任何其他 JavaScript 動畫函式庫。
import { AnimationCallbackEvent , Component , signal } from '@angular/core' ;
@ Component ({
selector: 'app-leave-binding' ,
templateUrl: 'leave-event.html' ,
styleUrls: [ 'leave-event.css' ],
})
export class LeaveEvent {
isShown = signal ( false );
toggle () {
this .isShown. update (( isShown ) => ! isShown);
}
leavingFn ( event : AnimationCallbackEvent ) {
// Example of calling GSAP
// gsap.to(event.target, {
// duration: 1,
// x: 100,
// // arrow functions are handy for concise callbacks
// onComplete: () => event.animationComplete()
// });
event. animationComplete ();
}
}
< h2 >< code >animate.leave</ code > Function Example</ h2 >
< button type = "button" class = "toggle-btn" (click) = "toggle()" >Toggle Element</ button >
@if ( isShown ()) {
< div class = "leave-container" (animate.leave) = "leavingFn($event)" >
< p >Goodbye</ p >
</ div >
}
:host {
display : block ;
height : 200 px ;
}
.leave-container {
border : 1 px solid #dddddd ;
margin-top : 1 em ;
padding : 20 px ;
font-weight : bold ;
font-size : 20 px ;
opacity : 1 ;
transition : opacity 200 ms ease-in ;
@starting-style {
opacity : 0 ;
}
}
.leave-container p {
margin : 0 ;
}
.leaving {
opacity : 0 ;
transform : translateY ( 20 px );
transition :
opacity 500 ms ease-out ,
transform 500 ms ease-out ;
}
.toggle-btn {
background : transparent ;
border : 1 px solid var ( --primary-contrast , black );
color : var ( --primary-contrast , black );
padding : 10 px 24 px ;
border-radius : 8 px ;
cursor : pointer ;
}
$event 物件的型別為 AnimationCallbackEvent 。它包含作為 target 的元素,並提供 animationComplete() 函式,用來在動畫結束時通知框架。
IMPORTANT: 使用 animate.leave 時,你必須 呼叫 animationComplete() 函式,Angular 才會移除元素。
若你在使用 animate.leave 時沒有呼叫 animationComplete(),Angular 會在延遲四秒後自動呼叫該函式。你可以以毫秒為單位提供 MAX_ANIMATION_TIMEOUT token,來設定這段延遲的長度。
你不能在同一個元件內同時使用舊版動畫與 animate.enter、animate.leave。這樣做會導致進入類別殘留在元素上,或離開的節點無法被移除。但在同一個應用程式 內同時使用舊版動畫與新的 animate.enter、animate.leave 動畫則沒有問題。唯一的例外是內容投影:若你把帶有舊版動畫的元件內容投影到帶有 animate.enter 或 animate.leave 的元件中(或反過來),其行為會與在同一個元件內一起使用相同,這是不支援的。
TestBed 內建支援在測試環境中啟用或停用動畫。CSS 動畫需要瀏覽器才能執行,許多相關 API 在測試環境中無法使用。TestBed 預設會在你的測試環境中停用動畫。
若你想測試動畫是否真的在瀏覽器測試(例如端到端測試)中播放,可以在測試設定中指定 animationsEnabled: true,將 TestBed 設定為啟用動畫。
TestBed . configureTestingModule ({animationsEnabled: true });
這樣就會讓測試環境中的動畫正常運作。
NOTE: 有些測試環境不會發出 animationstart、animationend 這類動畫事件,以及它們對應的轉場事件。
你可能也會對以下內容有興趣:
使用 CSS 的複雜動畫
路由轉場動畫