IMPORTANT: @angular/animations 套件現已棄用。Angular 團隊建議所有新程式碼改用原生 CSS 搭配 animate.enter 與 animate.leave 來做動畫。詳情請見新的進入與離開動畫指南。另可參閱從 Angular 動畫套件遷移,了解如何開始把應用程式遷移到純 CSS 動畫。
本指南深入說明 * 萬用字元與 void 等特殊轉場狀態,也會示範這些狀態如何用在元素進入與離開檢視的情境。
本節也會探討多個動畫觸發器、動畫回呼,以及使用關鍵影格的序列式動畫。
預先定義的狀態與萬用字元比對
在 Angular 中,轉場狀態可以透過 state() 函式明確定義,也可以使用預先定義的 * 萬用字元狀態與 void 狀態。
萬用字元狀態
星號 *,也就是 wildcard(萬用字元),可以比對任何動畫狀態。
這在定義不論 HTML 元素的起始或結束狀態為何都適用的轉場時非常好用。
例如,open => * 這個轉場會在元素的狀態從 open 變成其他任何狀態時套用。

下面是另一個程式碼範例,把萬用字元狀態和先前使用 open 與 closed 狀態的範例結合在一起。
不必再逐一為每一組狀態定義轉場配對,任何轉到 closed 的轉場都花 1 秒,任何轉到 open 的轉場都花 0.5 秒。
這樣一來,新增狀態時就不必為每個新狀態分別加上轉場。
open-close.ts
animations: [
trigger('openClose', [
// ...
state(
'open',
style({
height: '200px',
opacity: 1,
backgroundColor: 'yellow',
}),
),
state(
'closed',
style({
height: '100px',
opacity: 0.8,
backgroundColor: 'blue',
}),
),
transition('* => closed', [animate('1s')]),
transition('* => open', [animate('0.5s')]),
]),
],
使用雙箭頭語法,可以同時指定兩個方向的狀態轉場。
open-close.ts
transition('open <=> closed', [animate('0.5s')]),
搭配多個轉場狀態使用萬用字元狀態
在只有兩種狀態的按鈕範例中,萬用字元沒有那麼好用,因為可能的狀態只有 open 和 closed 兩種。
一般來說,當元素有多種可能切換到的狀態時,就適合使用萬用字元狀態。
如果按鈕可以從 open 變成 closed,或是像 inProgress 這類的其他狀態,使用萬用字元狀態就能減少需要撰寫的程式碼量。

open-close.ts
animations: [
trigger('openClose', [
// ...
state(
'open',
style({
height: '200px',
opacity: 1,
backgroundColor: 'yellow',
}),
),
state(
'closed',
style({
height: '100px',
opacity: 0.8,
backgroundColor: 'blue',
}),
),
transition('open => closed', [animate('1s')]),
transition('closed => open', [animate('0.5s')]),
transition('* => closed', [animate('1s')]),
transition('* => open', [animate('0.5s')]),
transition('open <=> closed', [animate('0.5s')]),
transition('* => open', [animate('1s', style({opacity: '*'}))]),
transition('* => *', [animate('1s')]),
只要兩個狀態之間發生任何變化,就會套用 * => * 轉場。
轉場會依定義的順序進行比對。
因此,你可以在 * => * 轉場之上再疊加其他轉場。
例如,先定義只套用於 open => closed 的樣式變更或動畫,再用 * => * 當作其他未特別列出的狀態配對的後備方案。
做法是把較具體的轉場列在 * => * 之前。
搭配樣式使用萬用字元
搭配樣式使用萬用字元 *,可以告訴動畫直接採用目前的樣式值,並以該值進行動畫。
當正在做動畫的狀態沒有在觸發器中宣告時,萬用字元就會當作後備值使用。
open-close.ts
transition('* => open', [animate('1s', style({opacity: '*'}))]),
Void 狀態
使用 void 狀態,可以為正在進入或離開頁面的元素設定轉場。
請參閱為進入與離開檢視的元素加上動畫。
結合萬用字元與 void 狀態
在同一個轉場中結合萬用字元與 void 狀態,可以觸發元素進入與離開頁面的動畫:
* => void轉場會在元素離開檢視時套用,不論它離開前處於什麼狀態void => *轉場會在元素進入檢視時套用,不論它進入時呈現什麼狀態- 萬用字元狀態
*可以比對 任何 狀態,包括void
為進入與離開檢視的元素加上動畫
本節說明如何為進入或離開頁面的元素加上動畫。
新增一個行為:
- 當你把一位英雄加入英雄清單時,它會從左側飛入頁面
- 當你從清單移除一位英雄時,它會往右側飛出頁面
hero-list-enter-leave.ts
animations: [
trigger('flyInOut', [
state('in', style({transform: 'translateX(0)'})),
transition('void => *', [style({transform: 'translateX(-100%)'}), animate(100)]),
transition('* => void', [animate(100, style({transform: 'translateX(100%)'}))]),
]),
],
在前面的程式碼中,當 HTML 元素尚未附加到檢視上時,你套用了 void 狀態。
別名 :enter 與 :leave
:enter 與 :leave 分別是 void => * 與 * => void 轉場的別名。
有幾個動畫函式會用到這些別名。
transition ( ':enter', [ … ] ); // alias for void => _
transition ( ':leave', [ … ] ); // alias for _ => void
要鎖定正在進入檢視的元素比較困難,因為它還不在 DOM 裡。
使用 :enter 與 :leave 這兩個別名,就能鎖定插入到檢視中、或從檢視移除的 HTML 元素。
搭配 *ngIf 與 *ngFor 使用 :enter 與 :leave
當任何 *ngIf 或 *ngFor 檢視放到頁面上時,會執行 :enter 轉場;當那些檢視從頁面移除時,則會執行 :leave 轉場。
IMPORTANT: 進入與離開的行為有時容易讓人混淆。
經驗法則是:任何由 Angular 加入 DOM 的元素,都會經過 :enter 轉場;只有由 Angular 直接從 DOM 移除的元素,才會經過 :leave 轉場。例如,某個元素的檢視會因為它的父元素被從 DOM 移除,而跟著從 DOM 消失。
這個範例有一個專門用於進入與離開動畫的觸發器,名為 myInsertRemoveTrigger。
HTML 範本包含以下程式碼。
insert-remove.html
@if (isShown) {
<div @myInsertRemoveTrigger class="insert-remove-container">
<p>The box is inserted</p>
</div>
}
在元件檔案中,:enter 轉場會把初始不透明度設為 0,接著在元素插入檢視時,以動畫把不透明度變成 1。
insert-remove.ts
trigger('myInsertRemoveTrigger', [
transition(':enter', [style({opacity: 0}), animate('100ms', style({opacity: 1}))]),
transition(':leave', [animate('100ms', style({opacity: 0}))]),
]),
請注意,這個範例不需要使用 state()。
轉場 :increment 與 :decrement
transition() 函式還接受 :increment 與 :decrement 這兩個選取器值。
當數值變大或變小時,可以用它們來啟動轉場。
hero-list-page.ts
trigger('filterAnimation', [
transition(':enter, * => 0, * => -1', []),
transition(':increment', [
query(
':enter',
[
style({opacity: 0, width: 0}),
stagger(50, [animate('300ms ease-out', style({opacity: 1, width: '*'}))]),
],
{optional: true},
),
]),
transition(':decrement', [
query(':leave', [stagger(50, [animate('300ms ease-out', style({opacity: 0, width: 0}))])]),
]),
]),
轉場中的布林值
如果觸發器的繫結值是布林值,就可以用比較 true 與 false(或 1 與 0)的 transition() 運算式來比對這個值。
open-close.html
<div [@openClose]="isOpen ? true : false" class="open-close-container">
</div>
在上方的程式碼片段中,HTML 範本把一個 <div> 元素繫結到名為 openClose 的觸發器,其狀態運算式為 isOpen,可能的值為 true 與 false。
這種模式可以替代建立 open 與 close 這類兩個具名狀態的做法。
在 @Component 中繼資料的 animations: 屬性裡,當狀態求值為 true 時,相關 HTML 元素的高度會是萬用字元樣式或預設值。
在這個例子中,動畫會採用元素在動畫開始之前原有的高度。
當元素處於 closed 狀態時,元素的高度會以動畫變成 0,讓它看不見。
open-close.ts
animations: [
trigger('openClose', [
state('true', style({height: '*'})),
state('false', style({height: '0px'})),
transition('false <=> true', animate(500)),
]),
],
多個動畫觸發器
你可以為一個元件定義不只一個動畫觸發器。 把動畫觸發器附加到不同元素上,而元素之間的父子關係會影響動畫執行的方式與時機。
父子動畫
在 Angular 中,每當一個動畫被觸發時,父動畫永遠優先,子動畫會被阻擋。
若要讓子動畫執行,父動畫必須查詢每一個包含子動畫的元素,接著再使用 animateChild() 函式讓這些動畫執行。
停用某個 HTML 元素上的動畫
有一個特殊的動畫控制繫結 @.disabled,可以放在 HTML 元素上,關閉該元素以及其所有巢狀元素的動畫。
當值為 true 時,@.disabled 繫結會阻止所有動畫呈現。
以下程式碼範例示範如何使用這項功能。
當 @.disabled 繫結為 true 時,@childAnimation 觸發器就不會啟動。
當 HTML 範本中的某個元素使用 @.disabled 主機繫結關閉動畫時,它內部所有元素的動畫也會一併關閉。
你無法在單一元素上選擇性地關閉多個動畫。
不過,仍可以用下列方式之一,讓已停用父元素之下的子動畫被選擇性地執行:
- 父動畫可以使用
query()函式,收集位於 HTML 範本中已停用區域內的內層元素。 那些元素仍然可以做動畫。
- 子動畫可以先被父動畫查詢出來,之後再用
animateChild()函式加上動畫
停用所有動畫
若要關閉 Angular 應用程式的所有動畫,把 @.disabled 主機繫結放在最上層的 Angular 元件上即可。
app.ts
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrls: ['app.css'],
imports: [RouterLink, RouterOutlet],
animations: [
slideInAnimation,
],
})
export class AppComponent {
@HostBinding('@.disabled')
public animationsDisabled = false;
}
HELPFUL: 在端對端(E2E)測試期間,把整個應用程式的動畫停用會很有幫助。
動畫回呼
動畫的 trigger() 函式會在動畫開始與結束時發出 callbacks(回呼)。
以下範例的元件包含一個 openClose 觸發器。
open-close.ts
@Component({
selector: 'app-open-close',
animations: [
trigger('openClose', [
// ...
]),
],
templateUrl: 'open-close.html',
styleUrls: ['open-close.css'],
})
export class OpenClose {
onAnimationEvent(event: AnimationEvent) {
}
}
在 HTML 範本中,動畫事件會透過 $event 以 @triggerName.start 與 @triggerName.done 的形式傳回,其中 triggerName 是所用觸發器的名稱。
在這個範例中,openClose 觸發器的寫法如下。
open-close.html
<div
[@openClose]="isOpen ? 'open' : 'closed'"
(@openClose.start)="onAnimationEvent($event)"
(@openClose.done)="onAnimationEvent($event)"
class="open-close-container"
>
</div>
動畫回呼的一種潛在用途,是用來掩蓋緩慢的 API 呼叫(例如資料庫查詢)所造成的等待感。 例如,可以為 InProgress 按鈕設定它自己的循環動畫,讓它在後端系統操作完成期間持續播放。
目前的動畫結束時,也可以呼叫另一個動畫。
例如,當 API 呼叫完成時,按鈕會從 inProgress 狀態轉到 closed 狀態。
動畫可以影響使用者,讓他們 感覺 操作速度變快了,即使實際上並沒有。
回呼也可以當作除錯工具,例如搭配 console.warn(),在瀏覽器的開發者 JavaScript 主控台中檢視應用程式的執行進度。
以下程式碼片段會為最初的範例(具有 open 與 closed 兩種狀態的按鈕)建立主控台記錄輸出。
open-close.ts
export class OpenClose {
onAnimationEvent(event: AnimationEvent) {
// openClose is trigger name in this example
console.warn(`Animation Trigger: ${event.triggerName}`);
// phaseName is "start" or "done"
console.warn(`Phase: ${event.phaseName}`);
// in our example, totalTime is 1000 (number of milliseconds in a second)
console.warn(`Total time: ${event.totalTime}`);
// in our example, fromState is either "open" or "closed"
console.warn(`From: ${event.fromState}`);
// in our example, toState either "open" or "closed"
console.warn(`To: ${event.toState}`);
// the HTML element itself, the button in this case
console.warn(`Element: ${event.element}`);
}
}
關鍵影格
若要建立由多個步驟依序執行的動畫,請使用 keyframes(關鍵影格)。
Angular 的 keyframe() 函式可以在單一計時區段內進行多次樣式變更。
例如,按鈕不必淡入淡出,而可以在 2 秒的單一時間跨度內多次變換顏色。

這段變色動畫的程式碼可能長這樣。
status-slider.ts
transition('* => active', [
animate(
'2s',
keyframes([
style({backgroundColor: 'blue'}),
style({backgroundColor: 'red'}),
style({backgroundColor: 'orange'}),
]),
),
Offset
關鍵影格包含一個 offset,用來定義每個樣式變更在動畫中發生的時間點。
Offset 是從 0 到 1 的相對量測值,標示動畫的起點與終點。如果至少使用了一次,每個關鍵影格步驟都應該套用 offset。
為關鍵影格定義 offset 是選擇性的。 如果你省略它們,系統會自動指派均勻間隔的 offset。 例如,三個沒有預先定義 offset 的關鍵影格,會分別得到 0、0.5 與 1 的 offset。 如果把前面範例中間那個轉場的 offset 指定為 0.8,可能會像這樣。

指定了 offset 的程式碼如下。
status-slider.ts
transition('* => active', [
animate(
'2s',
keyframes([
style({backgroundColor: 'blue', offset: 0}),
style({backgroundColor: 'red', offset: 0.8}),
style({backgroundColor: '#754600', offset: 1.0}),
]),
),
]),
transition('* => inactive', [
animate(
'2s',
keyframes([
style({backgroundColor: '#754600', offset: 0}),
style({backgroundColor: 'red', offset: 0.2}),
style({backgroundColor: 'blue', offset: 1.0}),
]),
),
]),
你可以在單一動畫中,把關鍵影格與 duration、delay 和 easing 結合使用。
帶有脈動效果的關鍵影格
在動畫過程中,於特定的 offset 定義樣式,就能用關鍵影格在動畫中建立脈動效果。
以下是一個使用關鍵影格建立脈動效果的範例:
- 原始的
open與closed狀態,高度、顏色與不透明度的原始變化在 1 秒的時間範圍內發生 - 在中間插入一段關鍵影格序列,讓按鈕在同樣的 1 秒時間範圍內呈現不規律的脈動

這段動畫的程式碼片段可能長這樣。
open-close.ts
trigger('openClose', [
state(
'open',
style({
height: '200px',
opacity: 1,
backgroundColor: 'yellow',
}),
),
state(
'close',
style({
height: '100px',
opacity: 0.5,
backgroundColor: 'green',
}),
),
// ...
transition('* => *', [
animate(
'1s',
keyframes([
style({opacity: 0.1, offset: 0.1}),
style({opacity: 0.6, offset: 0.2}),
style({opacity: 1, offset: 0.5}),
style({opacity: 0.2, offset: 0.7}),
]),
),
]),
]),
可動畫的屬性與單位
Angular 動畫建立在 Web 動畫之上,所以只要是瀏覽器認定可動畫的屬性,你都可以為它加上動畫。 這包括位置、尺寸、變換、顏色、框線等。 W3C 在其 CSS Transitions 頁面上維護了一份可動畫屬性的清單。
對於具有數值的屬性,請把值寫成字串、加上引號並附上適當的後綴,藉此定義單位:
50 像素:
'50px'相對字型大小:
'3em'百分比:
'100%'
你也可以把值寫成數字。在這種情況下,Angular 會假設預設單位是像素,也就是 px。
把 50 像素寫成 50,和寫成 '50px' 是相同的意思。
HELPFUL: 反之,字串 "50" 不會被視為有效值)。
使用萬用字元自動計算屬性
有時候,維度樣式屬性的值要到執行階段才知道。 例如,元素的寬度與高度往往取決於它們的內容或螢幕大小。 這類屬性用 CSS 來做動畫往往很棘手。
在這種情況下,你可以在 style() 底下使用特殊的萬用字元 * 屬性值。該樣式屬性的值會在執行階段計算出來,再填入動畫中。
以下範例有一個名為 shrinkOut 的觸發器,用在 HTML 元素離開頁面時。
動畫會採用元素離開前原有的高度,並以動畫從該高度變成 0。
hero-list-auto.ts
animations: [
trigger('shrinkOut', [
state('in', style({height: '*'})),
transition('* => void', [style({height: '*'}), animate(250, style({height: 0}))]),
]),
],
關鍵影格摘要
Angular 的 keyframes() 函式讓你可以在單一轉場中指定多個中途樣式。你也可以使用選擇性的 offset,定義每個樣式變更應該在動畫的哪個時間點發生。
更多 Angular 動畫相關資訊
你可能也會對以下內容有興趣: