如何在 Angular 中为 *ngFor 设置动画?

Posted

技术标签:

【中文标题】如何在 Angular 中为 *ngFor 设置动画?【英文标题】:How can I animate *ngFor in Angular? 【发布时间】:2016-09-10 01:58:29 【问题描述】:

我需要在填充和显示ngFor 列表时对其进行动画处理。每个元素都应该有一个过渡,比如说something like this。

我该怎么做?

【问题讨论】:

【参考方案1】:

它有一些问题,因为ngFor 做了一些多余的添加/删除,导致项目被动画化,而这不应该:

import Component from 'angular2/core';
import  Component, Directive, OnDestroy, Input  from 'angular2/core';

@Component(
    selector: 'my-app',
    template: `<div (click)="$event.preventDefault()">
        <button type="button" (click)="pushItem()">Push</button>
        <button type="button" (click)="removeItemLast()">Remove Last</button><br/>
        <button type="button" (click)="unshiftItem()">Unshift</button>
        <button type="button" (click)="removeItemFirst()">Remove First</button><br/>
        <ul>
          <li class="my-animation" *ngFor="#item of items">
            item.title
          </li>
        </ul>
      </div>`
)
export class AppComponent 
  private count:number = 1;
  public items: Array<any>;
  constructor()  
    console.clear(); 
    this.items = [];
    this.items.push(this.newItem());
    this.items.push(this.newItem());
    
    pushItem() 
        this.items.push(this.newItem());
    ,
    removeItemLast() 
      if(this.items.length > 0) this.items.splice(this.items.length - 1, 1);
    ,
    unshiftItem() 
        this.items.unshift(this.newItem());
    ,
    removeItemFirst() 
      if(this.items.length > 0) this.items.splice(0, 1);
    ,
    newItem() 
      return title: 'Item' + this.count++;
    


@keyframes MyAnimation 
  0% 
    padding-left: 100px;
  
  100% 
    padding-left: 0px;
   


.my-animation 
  animation: MyAnimation 1s;

来自https://github.com/angular/angular/issues/7239 的

Plunker example (RC.x) 说明了这个问题。

更新

很久以前就修复了

工作Demo on stackblitz

【讨论】:

如何在不改变数组的情况下执行此操作,例如如何使用 Observable> 为插入或更新设置动画 @maxou 我创建了一个新的 Plunker plnkr.co/edit/TMZKPbuzorVt9UsGJ2Nr?p=preview 你可以在这里清楚地看到问题:plnkr.co/edit/Xz48XN?p=preview当你点击'swap'时,所有索引发生变化的项目都会重新添加到dom中,无论'trackBy'子句如何 @JamiePate 当一次更改多个项目时,IterableDiffer 中似乎仍然存在错误。第一次删除,后来添加不会导致两者之间的所有内容都被删除并重新添加plnkr.co/edit/ky0sU4EV5NfXLTCkxC2d?p=preview(虽然后来添加的内容再次动画,但在此设置中是正确的) @GünterZöchbauer 是的,这就是我想要展示的。如果您改用@angular/animations,它似乎可以正常工作,所以这可能是要走的路。无论如何,对于这种复杂 DOM 交互的动画,动画 web api 比 css 容易得多。 (即使直接使用)【参考方案2】:

现在有guide to Angular's animation system。如果我们想做一些花哨的事情,这很有帮助,比如只为组件初始化后添加的元素做动画,而不是已经存在的元素。我已经修改了以前的答案以使用 Angular 2 方式。

Plunker:http://plnkr.co/edit/NAs05FiAVTlUjDOZfEsF?p=preview

import 
    Component,
    trigger, transition, animate, style, state
 from '@angular/core';

@Component(
    selector : 'my-app',
    animations: [
        trigger('growShrinkStaticStart', [
            state('in', style( height: '*', 'padding-top': '*', 'padding-bottom': '*', 'margin-top': '*', 'margin-bottom': '*' )),
            transition('* => void', [
                style( height: '*', 'padding-top': '*', 'padding-bottom': '*', 'margin-top': '*', 'margin-bottom': '*' ),
                animate("0.5s ease", style( height: '0', 'padding-top': '0', 'padding-bottom': '0', 'margin-top': '0', 'margin-bottom': '0' ))
            ]),
            transition('void => false', [
                /*no transition on first load*/
            ]),
            transition('void => *', [
                style( height: '0', 'padding-top': '0', 'padding-bottom': '0', 'margin-top': '0', 'margin-bottom': '0' ),
                animate("0.5s ease", style( height: '*', 'padding-top': '*', 'padding-bottom': '*', 'margin-top': '*', 'margin-bottom': '*' ))
            ])
        ])
    ],
    template : `<div (click)="$event.preventDefault()">
        <button type="button" (click)="pushItem()">Push</button>
        <button type="button" (click)="removeItemLast()">Remove Last</button><br/>
        <button type="button" (click)="unshiftItem()">Unshift</button>
        <button type="button" (click)="removeItemFirst()">Remove First</button><br/>
        <ul style="background: light-blue">
          <li *ngFor="let item of items" 
          [@growShrinkStaticStart]="animationInitialized.toString()" 
          (@growShrinkStaticStart.done)="animationInitialized = true"
          style="background-color:pink; border:1px dashed gray; overflow:hidden">
            <h3>item.title</h3><p>item.description</p>
          </li>
        </ul>
        <div>Footer</div>
      </div>`
)
export class AppComponent

    private count: number = 1;
    public items: Array < title: string, description: string > ;
    private animationInitialized: boolean = false;

    constructor() 
        this.items = [];
        this.items.push(this.newItem());
        this.items.push(this.newItem());
    

    pushItem() 
        this.items.push(this.newItem());
    

    removeItemLast() 
        if (this.items.length > 0)
            this.items.splice(this.items.length - 1, 1);
    

    unshiftItem() 
        this.items.unshift(this.newItem());
    

    removeItemFirst() 
        if (this.items.length > 0)
            this.items.splice(0, 1);
    

    newItem() 
        let d: string = "";
        let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZ    abcdefghijklmnopqrstuvwxyz0123456789 . ! ? ";

        for (let i = 0; i < Math.floor(Math.random() * 50000); i++)
            d += possible.charAt(Math.floor(Math.random() * possible.length));

        return  title : 'Item' + this.count++, description: d ;
    

【讨论】:

是否可以使用自定义转场stateChangeExpression 实现相同的转场效果 - 除了void =&gt; * (:entry) / * =&gt; void (:leave) for *ngFor ?例如定义transition('in =&gt; out',...transition('out =&gt; in',... 并在模板中使用[@growShrinkStaticStart]="transition" 并绑定到组件类中的transition 变量?我之所以问,是因为 *ngFor 进行了 DOM 添加和删除,而我没有成功通过自定义转换实现这一目标。 嗨,我正在使用 :leave ,如果我返回并刷新并分配一个 api 调用结果,它总是会触发动画。删除一项时是否可以只显示动画?

以上是关于如何在 Angular 中为 *ngFor 设置动画?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Angular 中为 ngfor 为 html 模板键入变量?

Angular2 单个 ngFor 用于多个数组

Angular,如何使用 *ngFor 或 HTML 表的任何重复设置行 ID

单行的Angular6多个ngFor-如何在一个ngFor中传递两个值

如何在使用 *ngFor 创建的 ionic 3 组件中为 css 类变量动态赋值

Angular:如何使用 *ngFor 将指令绑定到元素