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

时间:2019-01-03 20:57:58

标签: angular angular-material angular-animations

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

在Angular 4+中,我找不到轻松实现此目标的方法,到目前为止,我提出的唯一解决方案是以下解决方案,在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>

2 个答案:

答案 0 :(得分:1)

  

在Angular 4+中,我找不到轻松实现此目标的方法,到目前为止,我提出的唯一解决方案是以下解决方案,在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,
    },
  });

通过启动返回的RippleRef对象将告诉您波纹的当前状态。 RippleRenderer执行动画时,有一个rippleRef.state属性会随着时间的变化而变化。

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');
    });

我不确定上面的方法是否会起作用,因为第一个状态为HIDDEN,然后又转换回HIDDEN。我假设interval(100)将跳过第一个“隐藏”状态。

答案 1 :(得分:0)

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');
  }

}