Angular 復(fù)雜序列

2022-07-11 09:40 更新

前提條件

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

到目前為止,我們已經(jīng)學(xué)過了單個 HTML 元素的簡單動畫。Angular 還允許你在進入和離開頁面時播放 "動畫協(xié)調(diào)序列",比如當(dāng)整個網(wǎng)格或元素列表進入或離開頁面時,多個條目的動畫之間需要彼此協(xié)調(diào)時間。你可以選擇并行執(zhí)行多個動畫,或者按順序逐個運行離散動畫。

用來控制復(fù)雜動畫序列的函數(shù)如下:

函數(shù)

詳情

query()

用于查找一個或多個內(nèi)部 HTML 元素。

stagger()

用于為多元素動畫應(yīng)用級聯(lián)延遲。

?group()?

用于并行執(zhí)行多個動畫步驟。

sequence()

用于逐個順序執(zhí)行多個動畫步驟。

query() 函數(shù)

大多數(shù)復(fù)雜動畫都依賴 ?query()? 函數(shù)來查找子元素并對其應(yīng)用動畫,基本的例子是:

例子

詳情

query() 后跟 animate()

用于查詢簡單的 HTML 元素并直接對它們應(yīng)用動畫。

query() 后跟 animateChild()

用于查詢子元素,這些元素本身就應(yīng)用了動畫元數(shù)據(jù)并觸發(fā)這樣的動畫(否則將被當(dāng)前/父元素的動畫阻止)。

query() 的第一個參數(shù)是一個 css 選擇器字符串,它還可以包含以下 Angular 特定的標(biāo)記:

標(biāo)記

詳情

:enter
:leave

:enter
:leave

用于進入/離開元素。

:animating

對于當(dāng)前正在播放動畫的元素。

@*
@triggerName

對于具有任何(或特定)觸發(fā)器的元素。

:self

動畫元素本身。

進入和離開元素
并非所有子元素都會實際上被認(rèn)為是進入/離開;有時,這可能是違反直覺和令人困惑的。
你還可以在 Querying 選項卡下的動畫實時示例(在動畫介紹部分)中看到這方面的插圖。

使用 query() 和 stagger()(交錯)函數(shù)執(zhí)行多元素動畫

通過 ?query()? 查詢子元素后,?stagger()? 函數(shù)允許你定義每個查詢的動畫項之間的時間間隙,從而為元素之間延遲設(shè)置動畫。

下面的例子演示了如何使用 ?query()? 和 ?stagger()? 函數(shù)對依次添加的英雄列表從上到下播放動畫(有少許延遲)。

  • 用 ?query()? 查閱正在進入或離開頁面的任意元素。該查詢會找出那些符合某種匹配 CSS 選擇器的元素
  • 對每個元素,使用 ?style()? 為其設(shè)置初始樣式。使其變得透明,并使用 ?transform ?將其移出位置,以便它能滑入后就位。
  • 使用 ?stagger()? 來在每個動畫之間延遲 30 毫秒
  • 對屏幕上的每個元素,根據(jù)一條自定義緩動曲線播放 0.5 秒的動畫,同時將其淡入,而且逐步取消以前的位移效果
animations: [
  trigger('pageAnimations', [
    transition(':enter', [
      query('.hero', [
        style({opacity: 0, transform: 'translateY(-100px)'}),
        stagger(30, [
          animate('500ms cubic-bezier(0.35, 0, 0.25, 1)',
          style({ opacity: 1, transform: 'none' }))
        ])
      ])
    ])
  ]),

使用 group() 函數(shù)播放并行動畫

你已經(jīng)了解了如何在兩個連續(xù)的動畫之間添加延遲。不過你可能還想配置一些并行的動畫。比如,你可能希望為同一個元素的兩個 CSS 屬性設(shè)置動畫,但要為每個屬性使用不同的 ?easing ?函數(shù)。這時,你可以使用動畫函數(shù) ?group()?。

注意:
?group()?函數(shù)用于對動畫步驟進行分組,而不是針對動畫元素。

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

animations: [
  trigger('flyInOut', [
    state('in', style({
      width: '*',
      transform: 'translateX(0)', opacity: 1
    })),
    transition(':enter', [
      style({ width: 10, transform: 'translateX(50px)', opacity: 0 }),
      group([
        animate('0.3s 0.1s ease', style({
          transform: 'translateX(0)',
          width: '*'
        })),
        animate('0.3s ease', style({
          opacity: 1
        }))
      ])
    ]),
    transition(':leave', [
      group([
        animate('0.3s ease', style({
          transform: 'translateX(50px)',
          width: 10
        })),
        animate('0.3s 0.2s ease', style({
          opacity: 0
        }))
      ])
    ])
  ])
]

順序動畫與平行動畫

復(fù)雜動畫中可以同時發(fā)生很多事情。但是當(dāng)你要創(chuàng)建一個需要讓幾個子動畫逐個執(zhí)行的動畫時,該怎么辦呢?以前我們使用 ?group()? 來同時并行運行多個動畫。

第二個名叫 ?sequence()? 的函數(shù)會讓你一個接一個地運行這些動畫。在 ?sequence()? 中,這些動畫步驟由 ?style()? 或 ?animate()? 的函數(shù)調(diào)用組成。

  • ?style()? 用來立即應(yīng)用所指定的樣式數(shù)據(jù)。
  • ?animate()? 用來在一定的時間間隔內(nèi)應(yīng)用樣式數(shù)據(jù)。

過濾器動畫范例

來看看范例應(yīng)用中的另一個動畫。在 Filter/Stagger 頁,往 Search Heroes 文本框中輸入一些文本,比如 ?Magnet ?或 ?tornado?。

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

HTML 模板中包含一個名叫 ?filterAnimation ?的觸發(fā)器。

<label for="search">Search heroes: </label>
<input type="text" id="search" #criteria
       (input)="updateCriteria(criteria.value)"
       placeholder="Search heroes">

<ul class="heroes" [@filterAnimation]="heroesTotal">
  <li *ngFor="let hero of heroes" class="hero">
    <div class="inner">
      <span class="badge">{{ hero.id }}</span>
      <span class="name">{{ hero.name }}</span>
    </div>
  </li>
</ul>

該組件裝飾器中的 ?filterAnimation ?包含三個轉(zhuǎn)場。

@Component({
  animations: [
    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 })),
          ]),
        ])
      ]),
    ]),
  ]
})
export class HeroListPageComponent implements OnInit {
  heroesTotal = -1;

  get heroes() { return this._heroes; }
  private _heroes: Hero[] = [];

  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.heroesTotal !== newTotal) {
      this.heroesTotal = newTotal;
    } else if (!criteria) {
      this.heroesTotal = -1;
    }
  }
}

這個例子中的代碼包含下列任務(wù):

  • 當(dāng)用戶首次打開或?qū)Ш降酱隧撁鏁r,跳過所有動畫(該動畫會壓扁已經(jīng)存在的內(nèi)容,因此它只會作用于那些已經(jīng)存在于 DOM 中的元素)
  • 根據(jù)搜索框中的值過濾英雄

對于每次匹配:

  • 通過將元素的不透明度和寬度設(shè)置為 0 來隱藏正在離開 DOM 的元素
  • 對正在進入 DOM 的元素,播放一個 300 毫秒的動畫。在動畫期間,該元素采用其默認(rèn)寬度和不透明度。
  • 如果有多個匹配的元素正在進入或離開 DOM,則從頁面頂部的元素開始對每個元素進行交錯(stagger),每個元素之間的延遲為 50 毫秒

在重新排序列表的條目時設(shè)置動畫

盡管 Angular 開箱即用的支持 ?*ngFor? 列表項動畫,但如果只是它們的順序變化了,就無法支持。因為 Angular 會忘記哪個元素是哪個元素,從而導(dǎo)致這些動畫被破壞。幫助 Angular 跟蹤此類元素的唯一方法是將 ?TrackByFunction ?分配給 ?NgForOf ?指令。這可確保 Angular 始終知道哪個元素是哪個,從而允許它始終將正確的動畫應(yīng)用于正確的元素。

重要:
如果你需要為 ?*ngFor? 列表的條目設(shè)置動畫,并且此類條目的順序有可能在運行時更改,請始終使用 ?TrackByFunction?。

動畫序列總結(jié)

Angular 中這些用于多元素動畫的函數(shù),都要從 ?query()? 開始,查找出內(nèi)部元素,比如找出某個 ?<div>? 中的所有圖片。其余函數(shù) ?stagger()?、?group()? 和 ?sequence()? 會以級聯(lián)方式或你的自定義邏輯來控制要如何應(yīng)用多個動畫步驟。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號