requestAnimationFrameでアニメーショントリガーが作動する

公式ドキュメントの「ルーティング遷移のアニメーション」
https://angular.jp/guide/route-animations
を参考に、遷移時のアニメーションを制作しているのですが、
それとは別に、window.requestAnimationFrame()を使用し、全体をWebGLにて毎フレーム描画しています。
このとき、同コンポーネントのアニメーショントリガーに指定しているprepareRoute()が毎フレーム呼ばれてしまいます。
Angularのアニメーション機能とrequestAnimationFrameは併用しないほうが良いのでしょうか?
また、常時更新し続けたい場合のメソッド呼び出しに最適解などありますでしょうか?
再現プロジェクト : https://github.com/KzKousaka/AngularAnimationTest/

app.component.ts 内にprepareRoute()実体があります。

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  animations: [
    slideInAnimation
  ]
})
export class AppComponent {
  title = 'animation-test';

  prepareRoute(outlet: RouterOutlet) {
    let result = outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation'];
    console.warn(result);
    return result;
  }

  constructor() {
    this.mainLoop();
  }

  mainLoop() {
    window.requestAnimationFrame(this.mainLoop.bind(this));
  }
}

app.component.html内で[@routeAnimations]=“prepareRoute(outlet)というように呼び出しています。

<div [@routeAnimations]="prepareRoute(outlet)">
  <router-outlet #outlet="outlet"></router-outlet>
</div>

ご教示いただけますと幸いです。

1 Like

slackより転記です。

===
[jialipassion]さん回答

@KzKousaka 解決方法としては、下記のようにしたら、解決できます。

import {Component} from '@angular/core';
import {RouterOutlet} from '@angular/router';
import {slideInAnimation} from './animations';
declare let Zone: any;
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  animations: [slideInAnimation]
})
export class AppComponent {
  title = 'animation-test';
  prepareRoute(outlet: RouterOutlet) {
    let result = outlet && outlet.activatedRouteData &&
        outlet.activatedRouteData['animation'];
    console.warn(result);
    return result;
  }
  constructor() {
    this.mainLoop();
  }
  mainLoop() {
    (window as
     any)[Zone.__symbol__('requestAnimationFrame')](this.mainLoop.bind(this));
  }
}

ChangeDetectの必要がなさそうなので、
以下のようにngZoneを利用しても可能かと思います。

ChangeDetectが必要なタイミングで、zone.run()を呼び出し

import {Component, NgZone} from '@angular/core';
import {Router, RouterOutlet} from '@angular/router';
import {slideInAnimation} from './animations';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  animations: [
    slideInAnimation
  ]
})
export class AppComponent {
  title = 'animation-test';

  num = 1;

  prepareRoute(outlet: RouterOutlet) {
    const result = outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation'];
    console.warn(result);
    return result;
  }

  constructor(
    private zone: NgZone,
    private route: Router,
  ) {
    //
    this.zone.onMicrotaskEmpty.subscribe(() => {
      console.log('detect change');
    });
    this.zone.runOutsideAngular(() => { // ChangeDetectの対象外処理として、マーク
      const mainLoop = () => {
        console.log('called mainloop');
        this.num++;
        requestAnimationFrame(mainLoop.bind(this));
        if (this.num % 100 === 0) {  // 描画したい場合は、zone.runでchange detectを発生させる
          this.zone.run(() => {
          });
        }
      };
      mainLoop();
    });
  }
}

※参考URL