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

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

我该怎么做?

【问题讨论】:

    标签: angular transition angular-animations


    【解决方案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 调用结果,它总是会触发动画。删除一项时是否可以只显示动画?
    猜你喜欢
    • 2017-12-28
    • 2020-01-01
    • 2021-02-05
    • 1970-01-01
    • 2017-01-13
    • 2016-04-06
    • 2017-07-04
    • 1970-01-01
    相关资源
    最近更新 更多