Angular 5动画进入(void => state)有效但离开(state => void)不起作用

时间:2018-11-24 00:14:54

标签: css angular

在Angular中工作5.尝试从头开始创建轮播类型的图像浏览器。用户可以单击/滑动以查看下一个或上一个图像。

:enter类型的动画运行良好(在我的代码中显示为"void => next""void => prev")。但是,"next => void""prev => void"的动画过渡并未发生。

网络上的每个答案似乎都围绕着子组件,为元素display: block制作样式并在更改状态后调用detectChanges()。这不是子组件方案,我已经为元素分配了"display: block" css甚至可以将其包括在动画样式中,并且我确保正确添加了detectChanges()状态改变后。这些都不起作用。

我在某处读到一条评论,该内容发现detectChanges()不再具有:leave动画的技巧。他们的解决方法是在setTimeout()回调中包装将元素从DOM中删除的代码。 即使这不起作用

我只从https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts复制/粘贴整个代码块而已,只更改了变量名。但是,即使那样也不起作用!

这使我掉头发。请帮助我。

组件(打字稿中为Angular 5)

import { Component, OnInit, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
import { trigger, transition, style, animate, keyframes } from '@angular/animations';

type Orientation = ('prev' | 'next' | 'none');

@Component({
  selector: 'app-album-browser',
  templateUrl: './album-browser.component.html',
  styleUrls: ['./album-browser.component.scss'],
  animations: [
    trigger('animateCarousel',
    [
      transition('prev => void', // ------> leaving ------->
      [
        animate('500ms ease-in-out', keyframes([
          style({ opacity: 1.0, left: 0 }),
          style({ opacity: 0.0, left: 200 })
        ]))
      ]),
      transition('void => prev', // ----> entering ---->
      [
        animate('500ms ease-in-out', keyframes([
          style({ opacity: 0.0, left: -200, zIndex: 2 }),
          style({ opacity: 1.0, left: 0, zIndex: 2 })
        ]))
      ]),
      transition('next => void', // <------- leaving <-------
      [
        animate('500ms ease-in-out', keyframes([
          style({ opacity: 1.0, right: 0 }),
          style({ opacity: 0.0, right: 200 })
        ]))
      ]),
      transition('void => next', // <------- entering <--------
      [
        animate('500ms ease-in-out', keyframes([
          style({ opacity: 0.0, right: -200, zIndex: 2 }),
          style({ opacity: 1.0, right: 0, zIndex: 2 })
        ]))
      ])
    ])
  ]
})

export class AlbumBrowserComponent implements OnInit, AfterViewInit {

  private readonly SWIPE_ACTION = { LEFT: 'swipeleft', RIGHT: 'swiperight'};
  public readonly LEFT = 'LEFT';
  public readonly RIGHT = 'RIGHT';

  public orientation: Orientation = 'none';

  public images: string[] = [];
  public selectedImage: string[] = [];
  public changeDetectorRef: ChangeDetectorRef;

  constructor(changeDetectorRef: ChangeDetectorRef) {
    this.changeDetectorRef = changeDetectorRef;
    this.images.push('../../../assets/images/1.jpg');
    this.images.push('../../../assets/images/2.jpg');
    this.images.push('../../../assets/images/3.jpg');
    this.selectedImage.push(this.images[0]);
  }

  ngOnInit() {
  }

  public click(direction: string) {
    if (direction === this.LEFT) {
      this.swipe(this.SWIPE_ACTION.LEFT);
    }

    if (direction === this.RIGHT) {
      this.swipe(this.SWIPE_ACTION.RIGHT);
    }
  }

  public swipe(action = this.SWIPE_ACTION.RIGHT) {

    let res: string;
    const index = this.images.indexOf(this.selectedImage[0]);

    if (action === this.SWIPE_ACTION.LEFT) {
      this.orientation = 'next';
      this.selectedImage = [];
      this.changeDetectorRef.detectChanges();

      res = !!this.images[index + 1] ?
        this.images[index + 1] :
        this.images[0];
    }

    if (action === this.SWIPE_ACTION.RIGHT) {
      this.orientation = 'prev';
      this.selectedImage = [];
      this.changeDetectorRef.detectChanges();

      res = !!this.images[index - 1] ?
        this.images[index - 1] :
        this.images[this.images.length - 1];
    }

    this.selectedImage.push(res);
  }

}

模板

<div class="album-browser-container">
  <div class="left arrow small-glow" (click)="click(LEFT)"></div>
  <div class="viewport-frame glow">
    <div class="viewport">
      <div class="image-slider" 
        (swipeleft)="swipe($event.type)"
        (swiperight)="swipe($event.type)">
        <div class="carousel"
          *ngFor="let image of selectedImage">
          <div class="image-container"
            [@animateCarousel]="orientation">
            <img [src]="image" class="album-image">
          </div>
        </div>
      </div>
    </div>
  </div>
  <div class="right arrow small-glow" (click)="click(RIGHT)"></div>
</div>

2 个答案:

答案 0 :(得分:0)

TLDR;使用* ngIf而不是* ngFor并将更新后的selectedImage包装在setTimeout回调中。

我在调试解决方案时遇到了解决方案。在调试时,当在数组重置行上放置断点时,我看到了离开动画起作用。我在上面发布的GitHub条目(https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts)吸引了我大部分时间,似乎更高版本的Angular产生了以稍微不同的方式处理动画的需求。

我使用的是绑定到组件上名为“ showIt”的类成员的*ngFor,而不是在“图像容器” div中使用ngIf指令。我更新了方向并首先检测detectChanges()。然后将“ showIt”更新为FALSE,然后再次调用detectChanges()。这看起来似乎是多余的,但是当我省略对detectChanges()的增量调用时,几乎似乎在“队列”(由于缺少更好的术语)中留下了DOM更改,这些更改最终在后续对detectChanges()的调用中执行)。然后,我将images数组的目标索引存储在局部变量中,最后包装后续调用以在setTimeout回调中更新selectedImage,并将超时设置为与过渡动画相同的时间。

如果您在考虑“必须在setTimeout回调中包装代码有多么不雅”,我完全同意。但这似乎只是确保:enter:leave动画都能够蒸发的唯一方法。我最初问题的根本原因是,当selectedImage数组设置为空时,DOM的更新速度如此之快,以至于动画甚至在人眼无法察觉之前都被覆盖。

最终模板

<div class="album-browser-container">
  <div class="left arrow small-glow" (click)="click(LEFT)"></div>
  <div class="viewport-frame glow">
    <div class="viewport">
      <div class="image-slider" 
        (swipeleft)="swipe($event.type)"
        (swiperight)="swipe($event.type)">
        <div class="carousel">
          <div class="image-container"
            *ngIf="showIt"
            [@animateCarousel]="orientation">
            <img [src]="selectedImage[0]" 
              class="album-image"
              (swipeleft)="swipe($event.type)"
              (swiperight)="swipe($event.type)">
          </div>
        </div>
      </div>
    </div>
  </div>
  <div class="right arrow small-glow" (click)="click(RIGHT)"></div>
</div>

我的OP中的动画实际上是准确的。仅单击/滑动方法已更改。

组件(被截断)

public swipe(action = this.SWIPE_ACTION.RIGHT) {

    let res: string;

    if (action === this.SWIPE_ACTION.LEFT) {
      this.orientation = 'next';
      this.changeDetectorRef.detectChanges();
      this.showIt = false;
      this.changeDetectorRef.detectChanges();

      const index = this.images.indexOf(this.selectedImage[0]);

      res = !!this.images[index + 1] ?
        this.images[index + 1] :
        this.images[0];

      setTimeout(() => {
        this.selectedImage = [];
        this.selectedImage.push(res);
        this.showIt = true;
        this.changeDetectorRef.detectChanges();
      }, 300);

    }

    if (action === this.SWIPE_ACTION.RIGHT) {
      this.orientation = 'prev';
      this.changeDetectorRef.detectChanges();
      this.showIt = false;
      this.changeDetectorRef.detectChanges();

      const index = this.images.indexOf(this.selectedImage[0]);

      res = !!this.images[index - 1] ?
        this.images[index - 1] :
        this.images[this.images.length - 1];

      setTimeout(() => {
        this.selectedImage = [];
        this.selectedImage.push(res);
        this.showIt = true;
        this.changeDetectorRef.detectChanges();
      }, 300);
    }
  }

答案 1 :(得分:0)

你可以像这样使用 :leave 代替 'state => void',它会在元素从 DOM 中移除时触发这个转换(如果你使用结构指令喜欢 *ngIf)

transition(':leave', //your style goes here)