Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions packages/core/src/animation/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,6 @@ export const ANIMATIONS_DISABLED = new InjectionToken<boolean>(
},
);

export interface AnimationQueue {
queue: Set<Function>;
isScheduled: boolean;
}

/**
* A [DI token](api/core/InjectionToken) for the queue of all animations.
*/
export const ANIMATION_QUEUE = new InjectionToken<AnimationQueue>(
typeof ngDevMode !== 'undefined' && ngDevMode ? 'AnimationQueue' : '',
{
providedIn: 'root',
factory: () => {
return {
queue: new Set(),
isScheduled: false,
};
},
},
);

/**
* The event type for when `animate.enter` and `animate.leave` are used with function
* callbacks.
Expand Down
79 changes: 79 additions & 0 deletions packages/core/src/animation/queue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

import {afterNextRender} from '../render3/after_render/hooks';
import {InjectionToken, Injector} from '../di';
import {NodeAnimations} from './interfaces';

export interface AnimationQueue {
queue: Set<Function>;
isScheduled: boolean;
scheduler: Function | null;
}

/**
* A [DI token](api/core/InjectionToken) for the queue of all animations.
*/
export const ANIMATION_QUEUE = new InjectionToken<AnimationQueue>(
typeof ngDevMode !== 'undefined' && ngDevMode ? 'AnimationQueue' : '',
{
providedIn: 'root',
factory: () => {
return {
queue: new Set(),
isScheduled: false,
scheduler: null,
};
},
},
);

export function addToAnimationQueue(injector: Injector, animationFns: Function | Function[]) {
const animationQueue = injector.get(ANIMATION_QUEUE);
if (Array.isArray(animationFns)) {
for (const animateFn of animationFns) {
animationQueue.queue.add(animateFn);
}
} else {
animationQueue.queue.add(animationFns);
}
animationQueue.scheduler && animationQueue.scheduler(injector);
}

export function scheduleAnimationQueue(injector: Injector) {
const animationQueue = injector.get(ANIMATION_QUEUE);
// We only want to schedule the animation queue if it hasn't already been scheduled.
if (!animationQueue.isScheduled) {
afterNextRender(
() => {
animationQueue.isScheduled = false;
for (let animateFn of animationQueue.queue) {
animateFn();
}
animationQueue.queue.clear();
},
{injector},
);
animationQueue.isScheduled = true;
}
}

export function initializeAnimationQueueScheduler(injector: Injector) {
const animationQueue = injector.get(ANIMATION_QUEUE);
animationQueue.scheduler = scheduleAnimationQueue;
animationQueue.scheduler(injector);
}

export function queueEnterAnimations(
injector: Injector,
enterAnimations: Map<number, NodeAnimations>,
) {
for (const [_, nodeAnimations] of enterAnimations) {
addToAnimationQueue(injector, nodeAnimations.animateFns);
}
}
57 changes: 14 additions & 43 deletions packages/core/src/render3/instructions/animation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,19 @@
*/

import {
ANIMATION_QUEUE,
AnimationCallbackEvent,
AnimationFunction,
MAX_ANIMATION_TIMEOUT,
} from '../../animation/interfaces';
import {getLView, getCurrentTNode} from '../state';
import {RENDERER, INJECTOR, CONTEXT, LView, ANIMATIONS} from '../interfaces/view';
import {RENDERER, INJECTOR, CONTEXT, LView} from '../interfaces/view';
import {getNativeByTNode} from '../util/view_utils';
import {performanceMarkFeature} from '../../util/performance';
import {Renderer} from '../interfaces/renderer';
import {NgZone} from '../../zone';
import {determineLongestAnimation, allLeavingAnimations} from '../../animation/longest_animation';
import {TNode} from '../interfaces/node';
import {promiseWithResolvers} from '../../util/promise_with_resolvers';
import {Injector} from '../../di';
import {afterEveryRender} from '../after_render/hooks';

import {
addAnimationToLView,
Expand All @@ -47,6 +44,7 @@ import {
trackEnterClasses,
trackLeavingNodes,
} from '../../animation/utils';
import {initializeAnimationQueueScheduler, queueEnterAnimations} from '../../animation/queue';

/**
* Instruction to handle the `animate.enter` behavior for class bindings.
Expand Down Expand Up @@ -77,7 +75,11 @@ export function ɵɵanimateEnter(value: string | Function): typeof ɵɵanimateEn
runEnterAnimation(lView, tNode, value),
);

queueEnterAnimations(lView);
initializeAnimationQueueScheduler(lView[INJECTOR]);

// TODO(thePunderWoman): it's unclear why we need to queue animations here, but without this,
// animating through host bindings fails
queueEnterAnimations(lView[INJECTOR], getLViewEnterAnimations(lView));

return ɵɵanimateEnter; // For chaining
}
Expand Down Expand Up @@ -198,7 +200,11 @@ export function ɵɵanimateEnterListener(value: AnimationFunction): typeof ɵɵa
runEnterAnimationFunction(lView, tNode, value),
);

queueEnterAnimations(lView);
initializeAnimationQueueScheduler(lView[INJECTOR]);

// TODO(thePunderWoman): it's unclear why we need to queue animations here, but without this,
// animating through host bindings fails
queueEnterAnimations(lView[INJECTOR], getLViewEnterAnimations(lView));

return ɵɵanimateEnterListener;
}
Expand Down Expand Up @@ -244,7 +250,7 @@ export function ɵɵanimateLeave(value: string | Function): typeof ɵɵanimateLe
runLeaveAnimations(lView, tNode, value),
);

enableAnimationQueueScheduler(lView[INJECTOR]);
initializeAnimationQueueScheduler(lView[INJECTOR]);

return ɵɵanimateLeave; // For chaining
}
Expand Down Expand Up @@ -377,7 +383,7 @@ export function ɵɵanimateLeaveListener(value: AnimationFunction): typeof ɵɵa
runLeaveAnimationFunction(lView, tNode, value),
);

enableAnimationQueueScheduler(lView[INJECTOR]);
initializeAnimationQueueScheduler(lView[INJECTOR]);

return ɵɵanimateLeaveListener; // For chaining
}
Expand Down Expand Up @@ -465,38 +471,3 @@ function runLeaveAnimationFunction(
// Ensure cleanup if the LView is destroyed before the animation runs.
return {promise, resolve};
}

function queueEnterAnimations(lView: LView) {
enableAnimationQueueScheduler(lView[INJECTOR]);
const enterAnimations = lView[ANIMATIONS]?.enter;
if (enterAnimations) {
const animationQueue = lView[INJECTOR].get(ANIMATION_QUEUE);
for (const [_, nodeAnimations] of enterAnimations) {
for (const animateFn of nodeAnimations.animateFns) {
animationQueue.queue.add(animateFn);
}
}
}
}

function enableAnimationQueueScheduler(injector: Injector) {
const animationQueue = injector.get(ANIMATION_QUEUE);
// We only need to schedule the animation queue runner once per application.
if (!animationQueue.isScheduled) {
afterEveryRender(
() => {
runQueuedAnimations(injector);
},
{injector},
);
animationQueue.isScheduled = true;
}
}

function runQueuedAnimations(injector: Injector) {
const animationQueue = injector.get(ANIMATION_QUEUE);
for (let animateFn of animationQueue.queue) {
animateFn();
}
animationQueue.queue.clear();
}
18 changes: 2 additions & 16 deletions packages/core/src/render3/node_manipulation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ import {profiler} from './profiler';
import {ProfilerEvent} from './profiler_types';
import {getLViewParent, getNativeByTNode, unwrapRNode} from './util/view_utils';
import {allLeavingAnimations} from '../animation/longest_animation';
import {ANIMATION_QUEUE} from '../animation/interfaces';
import {Injector} from '../di';
import {addToAnimationQueue, queueEnterAnimations} from '../animation/queue';

const enum WalkTNodeTreeAction {
/** node create in the native environment. Run on initial creation. */
Expand All @@ -110,10 +110,7 @@ function maybeQueueEnterAnimation(
): void {
const enterAnimations = parentLView?.[ANIMATIONS]?.enter;
if (parent !== null && enterAnimations && enterAnimations.has(tNode.index)) {
const animationQueue = injector.get(ANIMATION_QUEUE);
for (const animateFn of enterAnimations.get(tNode.index)!.animateFns) {
animationQueue.queue.add(animateFn);
}
queueEnterAnimations(injector, enterAnimations);
}
}

Expand Down Expand Up @@ -182,17 +179,6 @@ function applyToElementOrContainer(
}
}

function addToAnimationQueue(injector: Injector, animationFns: Function | Function[]) {
const animationQueue = injector.get(ANIMATION_QUEUE);
if (Array.isArray(animationFns)) {
for (const animateFn of animationFns) {
animationQueue.queue.add(animateFn);
}
} else {
animationQueue.queue.add(animationFns);
}
}

/**
* Removes all DOM elements associated with a view.
*
Expand Down
Loading