77 */
88
99import {
10+ ANIMATION_QUEUE ,
1011 AnimationCallbackEvent ,
1112 AnimationFunction ,
1213 MAX_ANIMATION_TIMEOUT ,
1314} from '../../animation/interfaces' ;
1415import { getLView , getCurrentTNode } from '../state' ;
15- import { RENDERER , INJECTOR , CONTEXT , LView } from '../interfaces/view' ;
16+ import { RENDERER , INJECTOR , CONTEXT , LView , ANIMATIONS } from '../interfaces/view' ;
1617import { getNativeByTNode } from '../util/view_utils' ;
1718import { performanceMarkFeature } from '../../util/performance' ;
1819import { Renderer } from '../interfaces/renderer' ;
1920import { NgZone } from '../../zone' ;
2021import { determineLongestAnimation , allLeavingAnimations } from '../../animation/longest_animation' ;
2122import { TNode } from '../interfaces/node' ;
2223import { promiseWithResolvers } from '../../util/promise_with_resolvers' ;
24+ import { Injector } from '../../di' ;
25+ import { afterEveryRender } from '../after_render/hooks' ;
2326
2427import {
28+ addAnimationToLView ,
2529 areAnimationsDisabled ,
2630 areAnimationSupported ,
2731 assertAnimationTypes ,
@@ -66,12 +70,16 @@ export function ɵɵanimateEnter(value: string | Function): typeof ɵɵanimateEn
6670
6771 cancelLeavingNodes ( tNode , lView ) ;
6872
69- getLViewEnterAnimations ( lView ) . push ( ( ) => runEnterAnimation ( lView , tNode , value ) ) ;
73+ addAnimationToLView ( getLViewEnterAnimations ( lView ) , tNode , ( ) =>
74+ runEnterAnimation ( lView , tNode , value ) ,
75+ ) ;
76+
77+ queueEnterAnimations ( lView ) ;
7078
7179 return ɵɵanimateEnter ; // For chaining
7280}
7381
74- export function runEnterAnimation ( lView : LView , tNode : TNode , value : string | Function ) : void {
82+ export function runEnterAnimation ( lView : LView , tNode : TNode , value : string | Function ) {
7583 const nativeElement = getNativeByTNode ( tNode , lView ) as HTMLElement ;
7684
7785 ngDevMode && assertElementNodes ( nativeElement , 'animate.enter' ) ;
@@ -94,7 +102,7 @@ export function runEnterAnimation(lView: LView, tNode: TNode, value: string | Fu
94102
95103 const eventName = event instanceof AnimationEvent ? 'animationend' : 'transitionend' ;
96104 ngZone . runOutsideAngular ( ( ) => {
97- cleanupFns . push ( renderer . listen ( nativeElement , eventName , handleEnterAnimationEnd ) ) ;
105+ renderer . listen ( nativeElement , eventName , handleEnterAnimationEnd ) ;
98106 } ) ;
99107 } ;
100108
@@ -118,6 +126,7 @@ export function runEnterAnimation(lView: LView, tNode: TNode, value: string | Fu
118126 for ( const klass of activeClasses ) {
119127 renderer . addClass ( nativeElement , klass ) ;
120128 }
129+
121130 // In the case that the classes added have no animations, we need to remove
122131 // the classes right away. This could happen because someone is intentionally
123132 // preventing an animation via selector specificity.
@@ -182,15 +191,19 @@ export function ɵɵanimateEnterListener(value: AnimationFunction): typeof ɵɵa
182191
183192 cancelLeavingNodes ( tNode , lView ) ;
184193
185- getLViewEnterAnimations ( lView ) . push ( ( ) => runEnterAnimationFunction ( lView , tNode , value ) ) ;
194+ addAnimationToLView ( getLViewEnterAnimations ( lView ) , tNode , ( ) =>
195+ runEnterAnimationFunction ( lView , tNode , value ) ,
196+ ) ;
197+
198+ queueEnterAnimations ( lView ) ;
186199
187200 return ɵɵanimateEnterListener ;
188201}
189202
190203/**
191204 * runs enter animations when a custom function is provided
192205 */
193- function runEnterAnimationFunction ( lView : LView , tNode : TNode , value : AnimationFunction ) {
206+ function runEnterAnimationFunction ( lView : LView , tNode : TNode , value : AnimationFunction ) : void {
194207 const nativeElement = getNativeByTNode ( tNode , lView ) as HTMLElement ;
195208 ngDevMode && assertElementNodes ( nativeElement , 'animate.enter' ) ;
196209
@@ -224,19 +237,16 @@ export function ɵɵanimateLeave(value: string | Function): typeof ɵɵanimateLe
224237
225238 const tNode = getCurrentTNode ( ) ! ;
226239
227- getLViewLeaveAnimations ( lView ) . push ( ( ) =>
228- runLeaveAnimations ( lView , tNode , value , animationsDisabled ) ,
240+ addAnimationToLView ( getLViewLeaveAnimations ( lView ) , tNode , ( ) =>
241+ runLeaveAnimations ( lView , tNode , value ) ,
229242 ) ;
230243
244+ enableAnimationQueueScheduler ( lView [ INJECTOR ] ) ;
245+
231246 return ɵɵanimateLeave ; // For chaining
232247}
233248
234- function runLeaveAnimations (
235- lView : LView ,
236- tNode : TNode ,
237- value : string | Function ,
238- animationsDisabled : boolean ,
239- ) : Promise < void > {
249+ function runLeaveAnimations ( lView : LView , tNode : TNode , value : string | Function ) : Promise < void > {
240250 const { promise, resolve} = promiseWithResolvers < void > ( ) ;
241251 const nativeElement = getNativeByTNode ( tNode , lView ) as Element ;
242252
@@ -253,7 +263,6 @@ function runLeaveAnimations(
253263 tNode ,
254264 activeClasses ,
255265 renderer ,
256- animationsDisabled ,
257266 ngZone ,
258267 resolve ,
259268 ) ;
@@ -273,17 +282,11 @@ function animateLeaveClassRunner(
273282 tNode : TNode ,
274283 classList : string [ ] ,
275284 renderer : Renderer ,
276- animationsDisabled : boolean ,
277285 ngZone : NgZone ,
278286 resolver : VoidFunction ,
279287) {
280- if ( animationsDisabled ) {
281- longestAnimations . delete ( el ) ;
282- resolver ( ) ;
283- return ;
284- }
285-
286288 cancelAnimationsIfRunning ( el , renderer ) ;
289+ const cleanupFns : Function [ ] = [ ] ;
287290
288291 const handleOutAnimationEnd = ( event : AnimationEvent | TransitionEvent | CustomEvent ) => {
289292 // this early exit case is to prevent issues with bubbling events that are from child element animations
@@ -307,11 +310,14 @@ function animateLeaveClassRunner(
307310 }
308311 }
309312 resolver ( ) ;
313+ for ( const fn of cleanupFns ) {
314+ fn ( ) ;
315+ }
310316 } ;
311317
312318 ngZone . runOutsideAngular ( ( ) => {
313- renderer . listen ( el , 'animationend' , handleOutAnimationEnd ) ;
314- renderer . listen ( el , 'transitionend' , handleOutAnimationEnd ) ;
319+ cleanupFns . push ( renderer . listen ( el , 'animationend' , handleOutAnimationEnd ) ) ;
320+ cleanupFns . push ( renderer . listen ( el , 'transitionend' , handleOutAnimationEnd ) ) ;
315321 } ) ;
316322 trackLeavingNodes ( tNode , el ) ;
317323 for ( const item of classList ) {
@@ -326,6 +332,9 @@ function animateLeaveClassRunner(
326332 if ( ! longestAnimations . has ( el ) ) {
327333 clearLeavingNodes ( tNode , el ) ;
328334 resolver ( ) ;
335+ for ( const fn of cleanupFns ) {
336+ fn ( ) ;
337+ }
329338 }
330339 } ) ;
331340 } ) ;
@@ -359,7 +368,11 @@ export function ɵɵanimateLeaveListener(value: AnimationFunction): typeof ɵɵa
359368 const tNode = getCurrentTNode ( ) ! ;
360369 allLeavingAnimations . add ( lView ) ;
361370
362- getLViewLeaveAnimations ( lView ) . push ( ( ) => runLeaveAnimationFunction ( lView , tNode , value ) ) ;
371+ addAnimationToLView ( getLViewLeaveAnimations ( lView ) , tNode , ( ) =>
372+ runLeaveAnimationFunction ( lView , tNode , value ) ,
373+ ) ;
374+
375+ enableAnimationQueueScheduler ( lView [ INJECTOR ] ) ;
363376
364377 return ɵɵanimateLeaveListener ; // For chaining
365378}
@@ -416,3 +429,38 @@ function runLeaveAnimationFunction(
416429 // Ensure cleanup if the LView is destroyed before the animation runs.
417430 return promise ;
418431}
432+
433+ function queueEnterAnimations ( lView : LView ) {
434+ enableAnimationQueueScheduler ( lView [ INJECTOR ] ) ;
435+ const enterAnimations = lView [ ANIMATIONS ] ?. enter ;
436+ if ( enterAnimations ) {
437+ const animationQueue = lView [ INJECTOR ] . get ( ANIMATION_QUEUE ) ;
438+ for ( const [ _ , animateFns ] of enterAnimations ) {
439+ for ( const animateFn of animateFns ) {
440+ animationQueue . queue . add ( animateFn ) ;
441+ }
442+ }
443+ }
444+ }
445+
446+ function enableAnimationQueueScheduler ( injector : Injector ) {
447+ const animationQueue = injector . get ( ANIMATION_QUEUE ) ;
448+ // We only need to schedule the animation queue runner once per application.
449+ if ( ! animationQueue . isScheduled ) {
450+ afterEveryRender (
451+ ( ) => {
452+ runQueuedAnimations ( injector ) ;
453+ } ,
454+ { injector} ,
455+ ) ;
456+ animationQueue . isScheduled = true ;
457+ }
458+ }
459+
460+ function runQueuedAnimations ( injector : Injector ) {
461+ const animationQueue = injector . get ( ANIMATION_QUEUE ) ;
462+ for ( let animateFn of animationQueue . queue ) {
463+ animateFn ( ) ;
464+ }
465+ animationQueue . queue . clear ( ) ;
466+ }
0 commit comments