填寫這份《一分鐘調查》,幫我們(開發組)做得更好!去填寫Home

複雜動畫序列

Complex animation sequences

前提條件

Prerequisites

對下列概念有基本的理解:

A basic understanding of the following concepts:


到目前為止,我們已經學過了單個 HTML 元素的簡單動畫。Angular 還允許你在進入和離開頁面時播放 "動畫協調序列",比如當整個網格或元素列表進入或離開頁面時,多個條目的動畫之間需要彼此協調時間。你可以選擇並行執行多個動畫,或者按順序逐個執行離散動畫。

So far, we've learned simple animations of single HTML elements. Angular also lets you animate coordinated sequences, such as an entire grid or list of elements as they enter and leave a page. You can choose to run multiple animations in parallel, or run discrete animations sequentially, one following another.

用來控制複雜動畫序列的函式如下:

Functions that control complex animation sequences are as follows:

  • query() 用於查詢一個或多個內部 HTML 元素。

    query() finds one or more inner HTML elements.

  • stagger() 用於為多元素動畫應用級聯延遲。

    stagger() applies a cascading delay to animations for multiple elements.

  • group()用於並行執行多個動畫步驟。

    group()runs multiple animation steps in parallel.

  • sequence() 用於逐個順序執行多個動畫步驟。

    sequence() runs animation steps one after another.

使用 query()stagger()(交錯)函式執行多元素動畫

Animate multiple elements using query() and stagger() functions

query() 函式允許你查詢正在播放動畫的元素內部的元素。此函式會針對父元件中的特定 HTML 元素,並把動畫單獨應用於其中的每個元素。Angular 會智慧地處理初始化、收尾和清理工作,因為它負責協調頁面中的這些元素。

The query() function allows you to find inner elements within the element that is being animated. This function targets specific HTML elements within a parent component and applies animations to each element individually. Angular intelligently handles setup, teardown, and cleanup as it coordinates the elements across the page.

stagger() 函式允許你定義每個查詢出的動畫條目之間的時間間隔,從而讓這些條目動畫彼此錯開一定的延遲。

The stagger() function allows you to define a timing gap between each queried item that is animated and thus animates elements with a delay between them.

這個現場演練中的 Filter/Stagger 標籤顯示了一個帶有前導序列的英雄列表。整個英雄列表會級聯進入,從上到下逐個做輕微的延遲。

The Filter/Stagger tab in the live example shows a list of heroes with an introductory sequence. The entire list of heroes cascades in, with a slight delay from top to bottom.

下面的例子示範瞭如何在動畫元素的條目上使用 query()stagger() 函式。

The following example demonstrates how to use query() and stagger() functions on the entry of an animated element.

  • query() 查閱正在進入或離開頁面的任意元素。該查詢會找出那些符合某種匹配 CSS 選擇器的元素。

    Use query() to look for an element entering the page that meets certain criteria.

  • 對每個元素,使用 style() 為其設定初始樣式。使其不可見,並使用 transform 將其移出位置,以便它能滑入後就位。

    For each of these elements, use style() to set the same initial style for the element. Make it invisible and use transform to move it out of position so that it can slide into place.

  • 使用 stagger() 來在每個動畫之間延遲 30 毫秒。

    Use stagger() to delay each animation by 30 milliseconds.

  • 對螢幕上的每個元素,根據一條自訂緩動曲線播放 0.5 秒的動畫,同時將其淡入,而且逐步取消以前的位移效果。

    Animate each element on screen for 0.5 seconds using a custom-defined easing curve, simultaneously fading it in and un-transforming it.

animations: [ trigger('pageAnimations', [ transition(':enter', [ query('.hero, form', [ style({opacity: 0, transform: 'translateY(-100px)'}), stagger(-30, [ animate('500ms cubic-bezier(0.35, 0, 0.25, 1)', style({ opacity: 1, transform: 'none' })) ]) ]) ]) ]), ] }) export class HeroListPageComponent implements OnInit { @HostBinding('@pageAnimations') public animatePage = true; heroTotal = -1; get heroes() { return this._heroes; } private _heroes = []; ngOnInit() { this._heroes = HEROES; } updateCriteria(criteria: string) { criteria = criteria ? criteria.trim() : ''; this._heroes = HEROES.filter(hero => hero.name.toLowerCase().includes(criteria.toLowerCase())); const newTotal = this.heroes.length; if (this.heroTotal !== newTotal) { this.heroTotal = newTotal; } else if (!criteria) { this.heroTotal = -1; } } }
src/app/hero-list-page.component.ts
      
      animations: [
    trigger('pageAnimations', [
      transition(':enter', [
        query('.hero, form', [
          style({opacity: 0, transform: 'translateY(-100px)'}),
          stagger(-30, [
            animate('500ms cubic-bezier(0.35, 0, 0.25, 1)', style({ opacity: 1, transform: 'none' }))
          ])
        ])
      ])
    ]),
  ]
})
export class HeroListPageComponent implements OnInit {
  @HostBinding('@pageAnimations')
  public animatePage = true;

  heroTotal = -1;
  get heroes() { return this._heroes; }
  private _heroes = [];

  ngOnInit() {
    this._heroes = HEROES;
  }

  updateCriteria(criteria: string) {
    criteria = criteria ? criteria.trim() : '';

    this._heroes = HEROES.filter(hero => hero.name.toLowerCase().includes(criteria.toLowerCase()));
    const newTotal = this.heroes.length;

    if (this.heroTotal !== newTotal) {
      this.heroTotal = newTotal;
    } else if (!criteria) {
      this.heroTotal = -1;
    }
  }
}
    

使用 group() 函式播放並行動畫

Parallel animation using group() function

你已經瞭解瞭如何在兩個連續的動畫之間新增延遲。不過你可能還想配置一些並行的動畫。比如,你可能希望為同一個元素的兩個 CSS 屬性設定動畫,但要為每個屬性使用不同的 easing 函式。這時,你可以使用動畫函式 group()

You've seen how to add a delay between each successive animation. But you may also want to configure animations that happen in parallel. For example, you may want to animate two CSS properties of the same element but use a different easing function for each one. For this, you can use the animation group()function.

注意: group()函式用於對動畫步驟進行分組,而不是針對動畫元素。

Note: The group()function is used to group animation steps, rather than animated elements.

在下面的例子中,對 :enter:leave 使用分組,可以配置兩種不同的時序。它們會同時作用於同一個元素,但彼此獨立執行。

In the following example, using groups on both :enter and :leave allow for two different timing configurations. They're applied to the same element in parallel, but run independently.

animations: [ trigger('flyInOut', [ state('in', style({ width: 120, transform: 'translateX(0)', opacity: 1 })), transition('void => *', [ style({ width: 10, transform: 'translateX(50px)', opacity: 0 }), group([ animate('0.3s 0.1s ease', style({ transform: 'translateX(0)', width: 120 })), animate('0.3s ease', style({ opacity: 1 })) ]) ]), transition('* => void', [ group([ animate('0.3s ease', style({ transform: 'translateX(50px)', width: 10 })), animate('0.3s 0.2s ease', style({ opacity: 0 })) ]) ]) ]) ]
src/app/hero-list-groups.component.ts (excerpt)
      
      animations: [
  trigger('flyInOut', [
    state('in', style({
      width: 120,
      transform: 'translateX(0)', opacity: 1
    })),
    transition('void => *', [
      style({ width: 10, transform: 'translateX(50px)', opacity: 0 }),
      group([
        animate('0.3s 0.1s ease', style({
          transform: 'translateX(0)',
          width: 120
        })),
        animate('0.3s ease', style({
          opacity: 1
        }))
      ])
    ]),
    transition('* => void', [
      group([
        animate('0.3s ease', style({
          transform: 'translateX(50px)',
          width: 10
        })),
        animate('0.3s 0.2s ease', style({
          opacity: 0
        }))
      ])
    ])
  ])
]
    

順序動畫與平行動畫

Sequential vs. parallel animations

複雜動畫中可以同時發生很多事情。但是當你要建立一個需要讓幾個子動畫逐個執行的動畫時,該怎麼辦呢?以前我們使用 group()來同時並行執行多個動畫。

Complex animations can have many things happening at once. But what if you want to create an animation involving several animations happening one after the other? Earlier we used group()to run multiple animations all at the same time, in parallel.

第二個名叫 sequence() 的函式會讓你一個接一個地執行這些動畫。在 sequence() 中,這些動畫步驟由 style()animate() 的函式呼叫組成。

A second function called sequence() lets you run those same animations one after the other. Within sequence(), the animation steps consist of either style() or animate() function calls.

  • style() 用來立即應用所指定的樣式資料。

    Use style() to apply the provided styling data immediately.

  • animate() 用來在一定的時間間隔內應用樣式資料。

    Use animate() to apply styling data over a given time interval.

過濾器動畫範例

Filter animation example

我們來看看範例應用中的另一個動畫。在 Filter/Stagger 頁,往 Search Heroes 文字框中輸入一些文字,比如 Magnettornado

Let's take a look at another animation on the live example page. Under the Filter/Stagger tab, enter some text into the Search Heroes text box, such as Magnet or tornado.

過濾器會在你輸入時即時工作。每當你鍵入一個新字母時,就會有一些元素離開頁面,並且過濾條件也會逐漸變得更加嚴格。相反,當你刪除過濾器中的每個字母時,英雄列表也會逐漸重新進入頁面中。

The filter works in real time as you type. Elements leave the page as you type each new letter and the filter gets progressively stricter. The heroes list gradually re-enters the page as you delete each letter in the filter box.

HTML 範本中包含一個名叫 filterAnimation 的觸發器。

The HTML template contains a trigger called filterAnimation.

<ul class="heroes" [@filterAnimation]="heroTotal"> </ul>
src/app/hero-list-page.component.html
      
      <ul class="heroes" [@filterAnimation]="heroTotal">
</ul>
    

該元件檔案包含三個轉場。

The component file contains three transitions.

@Component({ animations: [ trigger('filterAnimation', [ transition(':enter, * => 0, * => -1', []), transition(':increment', [ query(':enter', [ style({ opacity: 0, width: '0px' }), 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: '0px' })), ]), ]) ]), ]), ] }) export class HeroListPageComponent implements OnInit { heroTotal = -1; }
src/app/hero-list-page.component.ts
      
      @Component({
  animations: [
    trigger('filterAnimation', [
      transition(':enter, * => 0, * => -1', []),
      transition(':increment', [
        query(':enter', [
          style({ opacity: 0, width: '0px' }),
          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: '0px' })),
          ]),
        ])
      ]),
    ]),
  ]
})
export class HeroListPageComponent implements OnInit {
  heroTotal = -1;
}
    

這個動畫包含下列動作:

The animation does the following:

  • 忽略使用者首次開啟或導航到此頁面時執行的任何動畫。過濾器窄化了已經存在的內容,因此它可以假設任何需要動畫的 HTML 元素都已經存在於 DOM 中了。

    Ignores any animations that are performed when the user first opens or navigates to this page. The filter narrows what is already there, so it assumes that any HTML elements to be animated already exist in the DOM.

  • 根據過濾器進行匹配。

    Performs a filter match for matches.

對於每次匹配:

For each match:

  • 透過將元素的不透明度和寬度設定為 0 來隱藏該元素,使其變得完全透明且無限窄。

    Hides the element by making it completely transparent and infinitely narrow, by setting its opacity and width to 0.

  • 元素中的動畫超過 300 毫秒。在動畫期間,元素採用其預設寬度和不透明度。

    Animates in the element over 300 milliseconds. During the animation, the element assumes its default width and opacity.

  • 如果有多個匹配的元素,則從頁面頂部的元素開始對每個元素進行交錯(stagger),每個元素之間的延遲為 50 毫秒。

    If there are multiple matching elements, staggers in each element starting at the top of the page, with a 50-millisecond delay between each element.

動畫序列總結

Animation sequence summary

Angular 中這些用於多元素動畫的函式,都要從 query() 開始,查找出內部元素,比如找出某個 <div> 中的所有圖片。其餘函式 stagger()group()sequence() 會以級聯方式或你的自訂邏輯來控制要如何應用多個動畫步驟。

Angular functions for animating multiple elements start with query() to find inner elements, for example gathering all images within a <div>. The remaining functions, stagger(), group(), and sequence(), apply cascades or allow you to control how multiple animation steps are applied.

關於 Angular 動畫的更多知識

More on Angular animations

你可能還對下列內容感興趣:

You may also be interested in the following: