如何检测 Angular 4+ 中的波纹何时完成动画?

How to detect when ripple has finished animating in Angular 4+?

所以我有一个待办事项列表,每个都有一个涟漪,当你点击待办事项时,我想将页面路由到一个新的 URL 但我希望涟漪动画在此之前完成。

在 Angular 4+ 中,我找不到一种方法来轻松地做到这一点,到目前为止我想出的唯一解决方案是以下一个,有没有 "right" 方法来在 Angular?

中执行此操作

组件:

export class TodoListComponent {
  public todos = this.todoService.todos$;

  @ViewChildren(MatRipple) private ripples: QueryList<MatRipple>;

  constructor(private todoService: TodoService, private router: Router) {}

  public onGoTo(event: MouseEvent, index: number, todo: Todo): void {
    const enterDuration = 300;
    const exitDuration = 50;

    const rippleRef = this.ripples
      .toArray()
      [index].launch(event.clientX, event.clientY, {
        animation: {
          enterDuration,
          exitDuration,
        },
      });
    setTimeout(() => rippleRef.fadeOut, enterDuration);
    const animationDuration = enterDuration + exitDuration;
    setTimeout(() => this.router.navigate([todo.id]), animationDuration);
  }
}

模板:

<mat-list>
    <mat-list-item
      *ngFor="let todo of (todos | async); let index = index"
      mat-ripple
      [matRippleDisabled]="true"
    >
      <div class="Todo">
        <div class="Todo__Label" (click)="onGoTo($event, index, todo)">
          {{ todo.title }}
        </div>
      </div>
    </mat-list-item>
  </mat-list>

Angular 支持 @animation.start@animation.done 来利用动画事件。

例如,您可以使用

(@routeAnimation.start)=onStart($event)(@routeAnimation.done)=onDone($event) 在您的组件中。

您可以在 Angular animations

找到更多信息

希望对您有所帮助!

示例代码:

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


@Component({
  selector: 'app-first',
  template: `
    <div class="w9914420" [@routeAnimation]="show" 
        (@routeAnimation.start)="onStart($event)"
        (@routeAnimation.done)="onDone($event)">
        <h2>
           first-component Works!
        </h2>
  </div>
  `,
  host: {
    '[@routeAnimation]': 'true',
    '[style.display]': "'block'",
    '[style.position]': "'absolute'"
  },
  animations: [
    trigger('routeAnimation', [
      state('*', style({transform: 'translateX(0)', opacity: 1})),
      transition('void => *', [style({transform: 'translateX(-100%)', opacity: 0}),animate(500)]),
      transition('* => void', animate(500, style({transform: 'translateX(100%)', opacity: 0})))
    ])
  ]
})
export class FirstComponent implements OnInit {

  constructor() { }

  ngOnInit() {

  }

  onStart($event) {
    // call this function at start of the animation.
    console.log('starting animation');
  }
  
  onDone($event) {
    // call this function at the end of the animation.
    console.log('the end of the animation');
  }

}

In Angular 4+ I can't find a way to do this easily, the only solution I have come up with so far is the following one, is there a "right" way to do this in Angular?

不,没有 Angular 方式,因为 MatRipple 组件不是以 Angular 方式完成的。波纹效果由 RippleRenderer 手动管理,看起来不像使用 Angular 动画。

https://github.com/angular/material2/blob/master/src/lib/core/ripple/ripple-renderer.ts

const rippleRef = this.ripples
  .toArray()
  [index].launch(event.clientX, event.clientY, {
    animation: {
      enterDuration,
      exitDuration,
    },
  });

launch返回的RippleRef对象会告诉你涟漪的当前状态。有一个 rippleRef.state 属性 会随着 RippleRenderer 执行动画而改变。

https://github.com/angular/material2/blob/master/src/lib/core/ripple/ripple-ref.ts

您可以尝试组合一个在波纹动画完成时发出的可观察对象,但您唯一的其他选择是使用 setTimeout().

 interval(100)
    .pipe(
      map(()=>rippleRef.state),
      distinctUntilChanged(),
      filter(state=>state === RippleState.HIDDEN),
      first()
    ).subscribe(()=> {
         console.log('ripple animation finished');
    });

我不确定以上是否可行,因为第一个状态是隐藏,然后它转换回隐藏。我假设 interval(100) 将跳过第一个隐藏状态。