|
5 | 5 | * Use of this source code is governed by an MIT-style license that can be |
6 | 6 | * found in the LICENSE file at https://angular.io/license |
7 | 7 | */ |
8 | | - |
| 8 | +import {Injectable} from '../di/metadata'; |
| 9 | +import {NgZone} from '../zone/ng_zone'; |
9 | 10 | import {AnimationPlayer} from './animation_player'; |
10 | 11 |
|
11 | | -let _queuedAnimations: AnimationPlayer[] = []; |
| 12 | +@Injectable() |
| 13 | +export class AnimationQueue { |
| 14 | + public entries: AnimationPlayer[] = []; |
12 | 15 |
|
13 | | -/** @internal */ |
14 | | -export function queueAnimation(player: AnimationPlayer) { |
15 | | - _queuedAnimations.push(player); |
16 | | -} |
| 16 | + constructor(private _zone: NgZone) {} |
| 17 | + |
| 18 | + enqueue(player: AnimationPlayer) { this.entries.push(player); } |
17 | 19 |
|
18 | | -/** @internal */ |
19 | | -export function triggerQueuedAnimations() { |
20 | | - // this code is wrapped into a single promise such that the |
21 | | - // onStart and onDone player callbacks are triggered outside |
22 | | - // of the digest cycle of animations |
23 | | - if (_queuedAnimations.length) { |
24 | | - Promise.resolve(null).then(_triggerAnimations); |
| 20 | + flush() { |
| 21 | + // given that each animation player may set aside |
| 22 | + // microtasks and rely on DOM-based events, this |
| 23 | + // will cause Angular to run change detection after |
| 24 | + // each request. This sidesteps the issue. If a user |
| 25 | + // hooks into an animation via (@anim.start) or (@anim.done) |
| 26 | + // then those methods will automatically trigger change |
| 27 | + // detection by wrapping themselves inside of a zone |
| 28 | + if (this.entries.length) { |
| 29 | + this._zone.runOutsideAngular(() => { |
| 30 | + // this code is wrapped into a single promise such that the |
| 31 | + // onStart and onDone player callbacks are triggered outside |
| 32 | + // of the digest cycle of animations |
| 33 | + Promise.resolve(null).then(() => this._triggerAnimations()); |
| 34 | + }); |
| 35 | + } |
25 | 36 | } |
26 | | -} |
27 | 37 |
|
28 | | -function _triggerAnimations() { |
29 | | - for (let i = 0; i < _queuedAnimations.length; i++) { |
30 | | - const player = _queuedAnimations[i]; |
31 | | - player.play(); |
| 38 | + private _triggerAnimations() { |
| 39 | + NgZone.assertNotInAngularZone(); |
| 40 | + |
| 41 | + while (this.entries.length) { |
| 42 | + const player = this.entries.shift(); |
| 43 | + // in the event that an animation throws an error then we do |
| 44 | + // not want to re-run animations on any previous animations |
| 45 | + // if they have already been kicked off beforehand |
| 46 | + if (!player.hasStarted()) { |
| 47 | + player.play(); |
| 48 | + } |
| 49 | + } |
32 | 50 | } |
33 | | - _queuedAnimations = []; |
34 | 51 | } |
0 commit comments