【问题标题】:Angular 5 animations enter (void => state) works but leave (state => void) not workingAngular 5 动画进入 (void => state) 工作但离开 (state => void) 不工作
【发布时间】:2018-11-24 00:14:54
【问题描述】:

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

:enter 类型的动画效果很好(在我的代码中显示为"void => next""void => prev")。但是,"next => void""prev => void" 动画转换不会发生。

网络上的每个答案似乎都围绕子组件,为元素display: block 制作样式,并在更改状态后调用detectChanges()。这不是子组件场景,我已经将 "display: block" css 分配给元素,甚至将其包含在动画样式中,并且我确保在状态更改后立即添加 detectChanges() .这些都没有奏效。

我在某处的评论中读到,detectChanges() 不再为 :leaving 动画解决问题。他们的解决方法是将用于从 DOM 中删除元素的代码包装在 setTimeout() 回调中。 即使这样也没有用

到了我只是从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>

【问题讨论】:

    标签: css angular


    【解决方案1】:

    TLDR;使用 *ngIf 代替 *ngFor 并将 selectedImage 的更新包装在 setTimeout 回调中。

    我在调试我想出的替代方案时遇到了解决方案。当我调试时,当我在数组重置行上放置一个断点时,我看到离开动画正在工作。我在上面发布的 GitHub 条目 (https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts) 让我了解了大部分情况,看来后来的 Angular 版本需要以稍微不同的方式处理动画。

    我没有在“image-container”div 中使用*ngFor 指令,而是使用绑定到组件上名为“showIt”的类成员的ngIf。我首先更新方向和 detectChanges()。然后将“showIt”更新为 FALSE,然后再调用 detectChanges()。这似乎是多余的,但是当我省略对detectChanges()的增量调用时,似乎“队列”中留下了DOM更改(因为缺少更好的术语),最终在随后调用detectChanges( )。然后,我将图像数组的目标索引存储在一个局部变量中,最后包装后续调用以在 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);
        }
      }
    

    【讨论】:

      【解决方案2】:

      您可以像这样使用 :leave 而不是使用 'state => void',这将在元素从 DOM 中移除时触发此转换(如果您使用 structural 指令,则会发生这种情况像 *ngIf)

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-03-28
        • 1970-01-01
        • 2021-07-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-09
        相关资源
        最近更新 更多