Angular Animation添加UI活潑的互動性

羅慧真 Anita Lo

  • 精誠資訊/恆逸教育訓練中心-資深講師
  • 技術分類:程式設計

 

 

App增添一些動畫效果不只是讓畫面變得好看,它還能幫助使用者更直覺地理解您的應用程式在做什麼。透過漸變、滑動、縮放等動畫效果,使用者可以感受到操作的回饋,讓介面看起來更自然、更有互動性。這一篇文章將透過一個簡單的範例,教您如何在Angular中使用Animation API做一個可開關顯示區塊的動畫元件。


Angular Animation是什麼?

Angular提供了內建的動畫API,讓您可以不依賴外部動畫函式庫,就能在元件中定義並控制動畫行為。這些動畫透過 @angular/animations 提供的一組函式來實現,其中包括:

  • trigger():定義一個動畫名稱與邏輯
  • state():定義動畫狀態與對應的樣式
  • style():指定 CSS 樣式
  • transition():描述狀態轉換與動畫時間
  • animate():控制動畫執行時長與緩動曲線

Angular Animation特別適合用於:

  • 展開/收合區塊
  • 加入/移除元件
  • 頁面切換與過場
  • 表單驗證與提示互動

您可以在元件的 @Component() 中,透過 animations: [] 區塊來註冊動畫邏輯。
接下來這個範例將帶您學會類似手風琴式的開關區塊效果,如下圖:



建置動畫的第一步

本範例是使用Angular 19 的環境建立專案,若您想試試看,建議至少使用 Angular 18 以上的版本,因為範例使用了 signal API 的相關函式:

ng new animatTips --minimal

首先,您需要在專案的 providers 宣告要使用的動畫元件,開啟 app.config.ts 在 providers 陣列中加入 provideAnimationsAsync() ,並匯入 provideAnimationsAsync() 的來源:

import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }), 
    provideAnimationsAsync(),  
    provideRouter(routes)
  ]
};


預備動畫元件

接著加入一個元件作為動畫元件:

ng g c box

修改 box.component.ts 內容如下,這是基本HTML內容並不具備動畫能力:

import { Component, computed, model } from '@angular/core';

@Component({
  selector: 'app-box',
  imports: [],
  template: `
    <div>boxState: {{ boxState() }}</div>
    @if (opened()){
      <div style="width: 500px; height: 300px; background-color: lightgray; box-shadow: 2px 2px 5px gray;">
          Hello 
      </div>
    }
  `,
  styles:[]
  ]
})
export class BoxComponent {
   opened = model(true);
   boxState = computed(() => this.opened() ? 'open' : 'closed');
}
  • opened,是一個使用 model(true) 初始化的屬性。這表示它是一個signal 的布林值模型,初始值為 true。model() 是 signal api 的功能之一,代表在元件中可input/output 的屬性,當該值改變時,會自動通知依賴它的其他部分進行更新。
  • boxState,是一個根據 opened 訊號變更時衍生的計算結果。computed() 是signal api 的功能之一,會根據另一個 signal 變數改變時進行計算。

在 app.component.ts 中使用 app-box,內容如下:

import { Component, computed, signal } from '@angular/core';
import { BoxComponent } from './box/box.component';

@Component({
  selector: 'app-root',
  imports: [BoxComponent],
  template: `
  <button (click)="toggle()">toggle </button>
  <app-box [opened]="isOpen()" ></app-box>`,
  styles:[]
})
export class AppComponent {
  title = 'AnimationBasic';
  isOpen = signal<boolean>(false);
  
  toggle() {
    this.isOpen.update((o)=>!o) ;    
  }

}
  • isOpen,是可寫入式的boolean訊號,預設值為 false 。
  • toggle(),是 button 按鈕的click 事件處理函示,控制 isOpen 的 true/false 變化,isOpen 是 signal 可寫變數,要使用 update () 函示進行變更 isOpen 的值。
  • <app-box [opened]="isOpen()" ></app-box> , isOpen 的值最後會帶入 app-box 的 opened 屬性。

使用 以下命令啟動程式:

ng s -o

結果如下,目前沒有任何動畫效果:


加入動畫效果

這個範例會有兩個狀態,一個是 open 、 一個是 closed ,當狀態改變時要改變style:

  • open 狀態, style 高度要調整為 300px ,不透明效果
  • closed 狀態, style 高度要調整為 0px ,完全透明效果

接下來修改 box.component.ts ,註記 @if (..) { ,,, } 區段,重新加入 div 區段:

@Component({
  selector: 'app-box',
  imports: [],
  template: `
    <div [@openClose]="boxState()" style="width: 500px; height: 300px; background-color: lightgray;
      box-shadow: 2px 2px 5px gray;">
      Hello 
    </div>
    <!-- <div>boxState: {{ boxState() }}</div>
    @if (opened()){
      <div style="width: 500px; height: 300px; background-color: lightgray; box-shadow: 2px 2px 5px gray;">
          Hello 
      </div>
    } -->
   
  `,
  styles: [],
  animations: [
    trigger('openClose', [
      state(
        'open',
        style({ height: '300px', opacity: 1 }),
      ),
      state(
        'closed',
        style({ height: '0px', opacity: 0 }),
      ),    
    ])
  ]
})

在 @Component 加入 animations 區段,陣列內加入一個成員 trigger() 。 trigger() 是宣告動畫觸發的名稱以及行為,第一個參數是 trigger 的名稱 -- openClose,第二個參數傳入則是動畫的元素,像是 state 、 transition 。 state() ,是動畫的狀態,第一個參數是狀態名稱,第二個參數會建立一組 CSS 樣式,用於當滿足此狀態時套用該樣式。 這個範例如上所述有兩個狀態,所以我們在 trigger 第二個參數中傳入兩個動畫元素,也就是 open / closed 狀態。

[@openClose], div 中的指令 @openClose 會對應到 trigger 的名稱,這裡傳入 boxState() ,它會根據opened布林訊號的改變計算值為 open 或是 closed 。因此觸動 trigger 中的 state 條件套用該 style 樣式。

加入動畫元素之後效果如下:


這樣的結果並不讓人滿意,最基本的動畫效果 … 感覺上要有一種慢慢變成 … 某種樣子,在此加一點轉場的效果,動畫味就十足了。

修改 animations 區段,在 trigger 的第二個參數陣列組中加入 transition:

@Component({
… 內容省略 …
 animations: [
    trigger('openClose', [
      state(
        'open',
        style({ height: '300px', opacity: 1 }),
      ),
      state(
        'closed',
        style({ height: '0px', opacity: 0 }),
      ) ,
      transition('open <=> closed', [animate('1s')])
    ])
  ]
})

transition(),是動畫元素之一,用來處理轉場效果,也就是 狀態A 到 狀態B 要花費的時間 (使用 animate 設定時間) ,這個範例的狀態是 open / closed ,這兩個狀態的中間符號可以是 => 、 <= 、 <=> ,這樣的符號應該很容易識讀,在此我們使用雙向箭頭「<=>」,代表不論是 open 到 closed 或是 closed 到 open ,第二個參數傳入的則是轉場完成的時間,這裡設定 1s,就是一秒的時間。

此時執行的結果將會是:


這個範例只是起點,未來您還可以加入滑動動畫、列表排序、頁面轉場等,讓使用者介面更具吸引力與互動性。更多關於Angular 動畫資訊可參考Angular官網(也是本篇文章的參考資源)

若您對於學習Angular有更多的興趣,歡迎參考以下課程可以了解更多的技巧喔!


您可在下列課程中了解更多技巧喔!