Angular 动画:错开动画子组件

Angular animation: stagger animate child components

几天来,我一直在努力让 Angular 动画为子组件(Content Children)工作,但尽管阅读了我能找到的所有内容,但我无法让它工作。

这个问题似乎大致在做我想做的事情,但我无法让它工作:

StackBlitz:https://stackblitz.com/edit/angular-anim-children

我的组件是:

父组件

import { Component } from '@angular/core';
import { trigger, transition, style, animate, query, stagger, group, animateChild } from '@angular/animations';

import { ListService } from '../services/list.service';

@Component({
  selector: 'app-list-container',
  template: `
    <button (click)="add()">Add</button>
    <hr>

    <app-list-item
      *ngFor="let item of list$ | async; let idx = index;"
      (delete)="remove(idx)"
      [@list]="(list$ | async).length"
      [title]="item.title"
      [index]="idx"
    ></app-list-item>
  `,
  styles: [`
    :host {
      display: block;
    }
  `],
  animations: [
    trigger('list', [
      transition('* => *', [
        query(':enter', stagger(50, animateChild()), { optional: true })
      ])
    ])
  ]
})
export class ListContainerComponent {
  readonly list$ = this.listService.list;
  readonly index: number;

  constructor(
    private readonly listService: ListService,
  ) { }

  add(): void {
    const counter = this.listService.counter;
    this.listService.add({ title: `Item ${counter + 1}` });
  }

  remove(index: number): void {
    this.listService.remove(index);
  }
}

子组件

import { Component, EventEmitter,  Input, Output } from '@angular/core';
import { trigger, transition, style, animate, query, stagger } from '@angular/animations';

@Component({
  selector: 'app-list-item',
  template: `
    <div class="list-item" [@animate]="true">
      {{title}}
      <button (click)="onDelete(index)">Delete</button>
    </div>
  `,
  styles: [
    `
      :host {
        display: block;
      }

      button {
        margin-left: 20px;
      }
    `
  ],
  animations: [
    trigger('animate', [
      transition(':enter', [
        style({ opacity: 0, transform: 'translateX(-10px)' }),
        animate('250ms', style({ opacity: 1, transform: 'translateX(0px)' }))
      ]),
      transition(':leave', [
        style({ opacity: 1 }),
        animate('250ms', style({ opacity: 0, transform: 'translateX(10px)' }))
      ])
    ])
  ],
})
export class ListItemComponent {
  @Input() title: string;
  @Input() index: number;

  @Output() delete = new EventEmitter<number>();

  onDelete(index: number): void {
    this.delete.emit(index);
  }
}

服务

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';

export interface IItem {
  title: string,
}

@Injectable()
export class ListService {
  private readonly _list = new BehaviorSubject<IItem[]>([]);
  readonly list = this._list.asObservable();

  counter = 0;

  get length() {
    return this._list.value.length;
  }

  add(item: IItem): void {
    this._list.next([item, ...this._list.value]);
    this.counter += 1;
  }

  remove(index: number): void {
    this._list.value.splice(index, 1);
  }

  clear(): void {
    this._list.next([]);
  }
}

您有一些错误,首先,父级动画(list 动画)应如下所示:

trigger("list", [
  transition("* => *", [query("@animate", stagger(50, animateChild()))])
])

请注意,您需要查询子动画才能错开它。

您应该将它添加到具有 animate 动画的元素的父元素上,因此在 list-container.component.ts 的模板中类似以下内容:

<div @list>
  <app-list-item
    *ngFor="let item of (list$ | async); let idx = index"
    (delete)="remove(idx)"
    [title]="item.title"
    [index]="idx"
  ></app-list-item>
</div>

[@animate]="true"可以变成@animate(这不是错误,而是多余的)

这是一个有效的 stackblitz