fix(animations): properly handle cancelled animation style application
This commit is contained in:

committed by
Jason Aden

parent
858dea98e5
commit
105e920b69
@ -18,7 +18,7 @@ import {ENTER_CLASSNAME, LEAVE_CLASSNAME, NG_ANIMATING_CLASSNAME, NG_ANIMATING_S
|
||||
import {AnimationDriver} from './animation_driver';
|
||||
import {getOrSetAsInMap, listenOnPlayer, makeAnimationEvent, normalizeKeyframes, optimizeGroupPlayer} from './shared';
|
||||
|
||||
const EMPTY_PLAYER_ARRAY: AnimationPlayer[] = [];
|
||||
const EMPTY_PLAYER_ARRAY: TransitionAnimationPlayer[] = [];
|
||||
const NULL_REMOVAL_STATE: ElementAnimationState = {
|
||||
namespaceId: '',
|
||||
setForRemoval: null,
|
||||
@ -708,7 +708,14 @@ export class TransitionAnimationEngine {
|
||||
|
||||
if (this._namespaceList.length &&
|
||||
(this.totalQueuedPlayers || this.collectedLeaveElements.length)) {
|
||||
players = this._flushAnimations(microtaskId);
|
||||
const cleanupFns: Function[] = [];
|
||||
try {
|
||||
players = this._flushAnimations(cleanupFns, microtaskId);
|
||||
} finally {
|
||||
for (let i = 0; i < cleanupFns.length; i++) {
|
||||
cleanupFns[i]();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this.collectedLeaveElements.length; i++) {
|
||||
const element = this.collectedLeaveElements[i];
|
||||
@ -737,7 +744,8 @@ export class TransitionAnimationEngine {
|
||||
}
|
||||
}
|
||||
|
||||
private _flushAnimations(microtaskId: number): TransitionAnimationPlayer[] {
|
||||
private _flushAnimations(cleanupFns: Function[], microtaskId: number):
|
||||
TransitionAnimationPlayer[] {
|
||||
const subTimelines = new ElementInstructionMap();
|
||||
const skippedPlayers: TransitionAnimationPlayer[] = [];
|
||||
const skippedPlayersMap = new Map<any, AnimationPlayer[]>();
|
||||
@ -745,7 +753,6 @@ export class TransitionAnimationEngine {
|
||||
const queriedElements = new Map<any, TransitionAnimationPlayer[]>();
|
||||
const allPreStyleElements = new Map<any, Set<string>>();
|
||||
const allPostStyleElements = new Map<any, Set<string>>();
|
||||
const cleanupFns: Function[] = [];
|
||||
|
||||
const bodyNode = getBodyNode();
|
||||
const allEnterNodes: any[] = this.collectedEnterElements.length ?
|
||||
@ -855,7 +862,6 @@ export class TransitionAnimationEngine {
|
||||
instruction.errors !.forEach(error => { msg += `- ${error}\n`; });
|
||||
});
|
||||
|
||||
cleanupFns.forEach(fn => fn());
|
||||
allPlayers.forEach(player => player.destroy());
|
||||
throw new Error(msg);
|
||||
}
|
||||
@ -1011,7 +1017,6 @@ export class TransitionAnimationEngine {
|
||||
player.play();
|
||||
});
|
||||
|
||||
cleanupFns.forEach(fn => fn());
|
||||
return rootPlayers;
|
||||
}
|
||||
|
||||
@ -1107,20 +1112,16 @@ export class TransitionAnimationEngine {
|
||||
const allSubElements = new Set<any>();
|
||||
const allNewPlayers = instruction.timelines.map(timelineInstruction => {
|
||||
const element = timelineInstruction.element;
|
||||
allConsumedElements.add(element);
|
||||
|
||||
// FIXME (matsko): make sure to-be-removed animations are removed properly
|
||||
const details = element[REMOVAL_FLAG];
|
||||
if (details && details.removedBeforeQueried) return new NoopAnimationPlayer();
|
||||
|
||||
const isQueriedElement = element !== rootElement;
|
||||
let previousPlayers: AnimationPlayer[] = EMPTY_PLAYER_ARRAY;
|
||||
if (!allConsumedElements.has(element)) {
|
||||
allConsumedElements.add(element);
|
||||
const _previousPlayers = allPreviousPlayersMap.get(element);
|
||||
if (_previousPlayers) {
|
||||
previousPlayers = _previousPlayers.map(p => p.getRealPlayer());
|
||||
}
|
||||
}
|
||||
const previousPlayers =
|
||||
(allPreviousPlayersMap.get(element) || EMPTY_PLAYER_ARRAY).map(p => p.getRealPlayer());
|
||||
|
||||
const preStyles = preStylesMap.get(element);
|
||||
const postStyles = postStylesMap.get(element);
|
||||
const keyframes = normalizeKeyframes(
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
import {AnimationPlayer} from '@angular/animations';
|
||||
|
||||
import {copyStyles, eraseStyles, setStyles} from '../../util';
|
||||
import {allowPreviousPlayerStylesMerge, copyStyles} from '../../util';
|
||||
|
||||
import {DOMAnimation} from './dom_animation';
|
||||
|
||||
@ -26,7 +26,7 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||
public time = 0;
|
||||
|
||||
public parentPlayer: AnimationPlayer|null = null;
|
||||
public previousStyles: {[styleName: string]: string | number};
|
||||
public previousStyles: {[styleName: string]: string | number} = {};
|
||||
public currentSnapshot: {[styleName: string]: string | number} = {};
|
||||
|
||||
constructor(
|
||||
@ -37,11 +37,12 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||
this._delay = <number>options['delay'] || 0;
|
||||
this.time = this._duration + this._delay;
|
||||
|
||||
this.previousStyles = {};
|
||||
previousPlayers.forEach(player => {
|
||||
let styles = player.currentSnapshot;
|
||||
Object.keys(styles).forEach(prop => this.previousStyles[prop] = styles[prop]);
|
||||
});
|
||||
if (allowPreviousPlayerStylesMerge(this._duration, this._delay)) {
|
||||
previousPlayers.forEach(player => {
|
||||
let styles = player.currentSnapshot;
|
||||
Object.keys(styles).forEach(prop => this.previousStyles[prop] = styles[prop]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _onFinish() {
|
||||
|
@ -213,3 +213,7 @@ const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
|
||||
export function dashCaseToCamelCase(input: string): string {
|
||||
return input.replace(DASH_CASE_REGEXP, (...m: any[]) => m[1].toUpperCase());
|
||||
}
|
||||
|
||||
export function allowPreviousPlayerStylesMerge(duration: number, delay: number) {
|
||||
return duration === 0 || delay === 0;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import {AUTO_STYLE, AnimationPlayer, NoopAnimationPlayer, ɵStyleData} from '@an
|
||||
|
||||
import {AnimationDriver} from '../../src/render/animation_driver';
|
||||
import {containsElement, invokeQuery, matchesElement} from '../../src/render/shared';
|
||||
|
||||
import {allowPreviousPlayerStylesMerge} from '../../src/util';
|
||||
|
||||
/**
|
||||
* @experimental Animation support is experimental.
|
||||
@ -56,12 +56,15 @@ export class MockAnimationPlayer extends NoopAnimationPlayer {
|
||||
public duration: number, public delay: number, public easing: string,
|
||||
public previousPlayers: any[]) {
|
||||
super();
|
||||
previousPlayers.forEach(player => {
|
||||
if (player instanceof MockAnimationPlayer) {
|
||||
const styles = player.currentSnapshot;
|
||||
Object.keys(styles).forEach(prop => this.previousStyles[prop] = styles[prop]);
|
||||
}
|
||||
});
|
||||
|
||||
if (allowPreviousPlayerStylesMerge(duration, delay)) {
|
||||
previousPlayers.forEach(player => {
|
||||
if (player instanceof MockAnimationPlayer) {
|
||||
const styles = player.currentSnapshot;
|
||||
Object.keys(styles).forEach(prop => this.previousStyles[prop] = styles[prop]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.totalTime = delay + duration;
|
||||
}
|
||||
|
Reference in New Issue
Block a user