Как определить, когда рябь закончила анимацию в Angular 4+?

Итак, у меня есть список задач, каждая из которых имеет пульсацию, и когда вы нажимаете задачу, я хочу перенаправить страницу на новый 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>

person Dominic Santos    schedule 03.01.2019    source источник


Ответы (2)


В 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, возвращаемый запуском, сообщит вам, каково текущее состояние пульсации. Существует свойство 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) пропустит первое СКРЫТОЕ состояние.

person Reactgular    schedule 03.01.2019

Angular поддерживал @animation.start и @animation.done для доступа к событиям анимации.

Например, вы можете использовать

(@routeAnimation.start)=onStart($event) и (@routeAnimation.done)=onDone($event) в вашем компоненте.

Дополнительную информацию можно найти на странице анимации Angular.

Надеюсь, это поможет!

Пример кода:

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

}

person Mile Mijatović    schedule 03.01.2019