代码之家  ›  专栏  ›  技术社区  ›  Rob Monhemius

如何在对象引用更改时设置类似于:进入和:离开的动画

  •  0
  • Rob Monhemius  · 技术社区  · 5 年前

    以下是一些工作代码:

    闪电战 : https://stackblitz.com/edit/angular-ivy-tt9vjd?file=src/app/app.component.ts

    应用程序。组成部分html

    <button (click)='swap()'>Swap object</button>
    <div @div *ngIf='object'>{{ object.data }}</div>
    

    应用程序。组成部分css

    div {
      width: 100px;
      height: 100px;
      background: purple;
      display: flex;
      align-items: center;
      justify-content: center;
      color: antiquewhite;
    }
    

    应用程序。组成部分ts

    import { animate, style, transition, trigger } from "@angular/animations";
    import { Component } from "@angular/core";
    import { interval } from "rxjs";
    import { first } from "rxjs/operators";
    
    @Component({
      selector: "my-app",
      templateUrl: "./app.component.html",
      styleUrls: ["./app.component.css"],
      animations: [
        trigger("div", [
          transition(":enter", [
            style({ transform: "scale(0)" }),
            animate("250ms 0ms ease-out", style({ transform: "scale(1)" }))
          ]),
          transition(":leave", [
            style({ transform: "scale(1)" }),
            animate("250ms 0ms ease-in", style({ transform: "scale(0)" }))
          ])
        ])
      ]
    })
    export class AppComponent {
      object: any = { data: "DIV_1" };
    
      swap() {
        this.object = null;
        interval(0)
          .pipe(first())
          .subscribe(() => {
            this.object = { data: "DIV_2" };
          });
    
        // DESIRED CODE INSTEAD OF THE ABOVE
        // this.object = { data: "DIV_2" };
      }
    }
    

    该代码的问题在于,中间人 null 必须引入国家。因此,为了“让它工作”,我将表示代码与逻辑混合在一起。这违反了良好的封装实践,并给代码增加了不必要的复杂性。

    如何在同一个环境中单独使用代码来实现相同的结果 animations 装修工的财产?

    要求

    • 检测对象引用中的更改。
    • 对对象引用更改的反应类似于 :enter 和 :leave 工作首先制作动画 DIV_1 出来,然后制作动画 DIV_2 在里面
    • 将有关动画的任何内容封装到 动画 密码所以交换功能应该是: swap(){this.object = { data: "DIV_2" };}
    0 回复  |  直到 5 年前
        1
  •  1
  •   Francesco Lisandro    5 年前

    所以,我花了一点时间来解决这个问题,但我找到了一个可能的解决方案,可以帮助你。您应该创建一个自定义指令来代替经典指令 ngIf .

    import {
      Directive,
      Input,
      TemplateRef,
      ViewContainerRef
    } from '@angular/core';
    
    @Directive({
      selector: '[ngIfAnimation]'
    })
    export class NgIfAnimationDirective {
      private value: any;
      private hasView = false;
    
      constructor(
        private view: ViewContainerRef,
        private tmpl: TemplateRef<any>
      ) { }
    
      @Input() set ngIfAnimation(val: any) {
        if (!this.hasView) {
          this.view.createEmbeddedView(this.tmpl);
          this.hasView = true;
        } else if (val !== this.value) {
          this.view.clear();
          this.view.createEmbeddedView(this.tmpl);
          this.value = val;
        }
      }
    }
    

    快速:我们清除当前视图,实例化一个嵌入式视图,并在每次发生更改时将其插入到div中。 关于您的组件,您的动画将如下所示(当然,您可以更改它们):

    animations: [
        trigger('div', [
          state('void', style({ transform: 'scale(0)' })),
          state('*', style({transform: 'scale(1)' })),
          transition('void => *', [animate('0.2s 0.2s ease-in')]),
          transition('* => void', [animate('0.2s ease-in')])
        ])
      ],
    

    还有你的模板:

    <div [@div] *ngIfAnimation="object">{{ object.data }}</div>
    

    记得把你的衣服脱下来 *ngIf 因为一个元素上不能有多个模板绑定。

    闪电战: https://stackblitz.com/edit/angular-ivy-hc8snt?file=src/app/app.component.html

        2
  •  0
  •   Rob Monhemius    5 年前

    只是想和大家分享我的结局。这个解决方案的灵感主要来自弗朗西斯科·利桑德罗(Francesco Lisandro)的答案。

    • 创建了一个结构指令
    • 该指令接受一个具有两个属性的对象
      • 数据 :引用更改的对象
      • 叶片过渡期 :延迟插入空状态的持续时间
    • 该指令在旧对象之后和创建新对象之前插入一个空状态。在leaveTransitionDuration之后插入void状态。
    • 当引用从falsy值更改为对象时,该指令不应用leaveTransitionDuration。那么普通人呢 :enter 动画不会被延迟。

    应用程序。组成部分html

    <div @div *ngIfAnimation="{data: object, leaveTransitionDuration: 2000}">{{ object.data }}</div>
    

    应用程序。组成部分ts

    import { Component } from "@angular/core";
    import { animate, style, transition, trigger } from "@angular/animations";
    
    @Component({
      selector: "my-app",
      templateUrl: "./app.component.html",
      styleUrls: ["./app.component.css"],
      animations: [
        trigger("div", [
          transition(":enter", [
            style({ transform: "scale(0)" }),
            animate("500ms 0s ease-in", style({ transform: "scale(1)" }))
          ]),
          transition(":leave", [
            style({ transform: "scale(1)" }),
            animate("2s ease-in", style({ transform: "scale(0)" }))
          ])
        ])
      ]
    })
    export class AppComponent {
      object: any = { data: "DIV_1" };
    
      swap() {
        this.object = { data: "DIV_2" };
      }
    }
    

    这是我的梦想。diretive。ts

    import { Directive, Input, TemplateRef, ViewContainerRef } from "@angular/core";
    import { interval } from "rxjs";
    import { first } from "rxjs/operators";
    
    @Directive({
      selector: "[ngIfAnimation]"
    })
    export class NgIfAnimationDirective {
      constructor(private view: ViewContainerRef, private tmpl: TemplateRef<any>) {}
      private old_data = null;
      private leaveTransitionDuration: number;
    
      @Input() set ngIfAnimation(object: {
        data: any;
        leaveTransitionDuration: number;
      }) {
        this.leaveTransitionDuration = object.leaveTransitionDuration;
        this.view.clear();
        if (object.data) this.add_new_view();
        this.old_data = object.data;
      }
    
      private get delay(): number {
        return this.old_data ? this.leaveTransitionDuration : 0;
      }
      private add_new_view(): void {
        interval(this.delay) // timeout until `:leave` animation is ran
          .pipe(first())
          .subscribe(() => {
            this.view.createEmbeddedView(this.tmpl);
          });
      }
    }
    

    查看Stackblitz的演示: https://stackblitz.com/edit/angular-ivy-t7ea53?file=src/app/ngIfAnimation.directive.ts

    注意:确保休假过渡持续时间等于:休假过渡的持续时间。

    推荐文章