Revert "fix(animations): always fire inner trigger callbacks even if blocked by parent animations (#19753)"
This reverts commit d47b2a6f70
.
This commit is contained in:
parent
043e408805
commit
f8658cdc38
|
@ -986,15 +986,11 @@ export class TransitionAnimationEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
const allPreviousPlayersMap = new Map<any, TransitionAnimationPlayer[]>();
|
const allPreviousPlayersMap = new Map<any, TransitionAnimationPlayer[]>();
|
||||||
// this map works to tell which element in the DOM tree is contained by
|
let sortedParentElements: any[] = [];
|
||||||
// which animation. Further down below this map will get populated once
|
|
||||||
// the players are built and in doing so it can efficiently figure out
|
|
||||||
// if a sub player is skipped due to a parent player having priority.
|
|
||||||
const animationElementMap = new Map<any, any>();
|
|
||||||
queuedInstructions.forEach(entry => {
|
queuedInstructions.forEach(entry => {
|
||||||
const element = entry.element;
|
const element = entry.element;
|
||||||
if (subTimelines.has(element)) {
|
if (subTimelines.has(element)) {
|
||||||
animationElementMap.set(element, element);
|
sortedParentElements.unshift(element);
|
||||||
this._beforeAnimationBuild(
|
this._beforeAnimationBuild(
|
||||||
entry.player.namespaceId, entry.instruction, allPreviousPlayersMap);
|
entry.player.namespaceId, entry.instruction, allPreviousPlayersMap);
|
||||||
}
|
}
|
||||||
|
@ -1045,7 +1041,6 @@ export class TransitionAnimationEngine {
|
||||||
|
|
||||||
const rootPlayers: TransitionAnimationPlayer[] = [];
|
const rootPlayers: TransitionAnimationPlayer[] = [];
|
||||||
const subPlayers: TransitionAnimationPlayer[] = [];
|
const subPlayers: TransitionAnimationPlayer[] = [];
|
||||||
const NO_PARENT_ANIMATION_ELEMENT_DETECTED = {};
|
|
||||||
queuedInstructions.forEach(entry => {
|
queuedInstructions.forEach(entry => {
|
||||||
const {element, player, instruction} = entry;
|
const {element, player, instruction} = entry;
|
||||||
// this means that it was never consumed by a parent animation which
|
// this means that it was never consumed by a parent animation which
|
||||||
|
@ -1057,41 +1052,29 @@ export class TransitionAnimationEngine {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this will flow up the DOM and query the map to figure out
|
|
||||||
// if a parent animation has priority over it. In the situation
|
|
||||||
// that a parent is detected then it will cancel the loop. If
|
|
||||||
// nothing is detected, or it takes a few hops to find a parent,
|
|
||||||
// then it will fill in the missing nodes and signal them as having
|
|
||||||
// a detected parent (or a NO_PARENT value via a special constant).
|
|
||||||
let parentWithAnimation: any = NO_PARENT_ANIMATION_ELEMENT_DETECTED;
|
|
||||||
if (animationElementMap.size > 1) {
|
|
||||||
let elm = element;
|
|
||||||
const parentsToAdd: any[] = [];
|
|
||||||
while (elm = elm.parentNode) {
|
|
||||||
const detectedParent = animationElementMap.get(elm);
|
|
||||||
if (detectedParent) {
|
|
||||||
parentWithAnimation = detectedParent;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
parentsToAdd.push(elm);
|
|
||||||
}
|
|
||||||
parentsToAdd.forEach(parent => animationElementMap.set(parent, parentWithAnimation));
|
|
||||||
}
|
|
||||||
|
|
||||||
const innerPlayer = this._buildAnimation(
|
const innerPlayer = this._buildAnimation(
|
||||||
player.namespaceId, instruction, allPreviousPlayersMap, skippedPlayersMap, preStylesMap,
|
player.namespaceId, instruction, allPreviousPlayersMap, skippedPlayersMap, preStylesMap,
|
||||||
postStylesMap);
|
postStylesMap);
|
||||||
|
|
||||||
player.setRealPlayer(innerPlayer);
|
player.setRealPlayer(innerPlayer);
|
||||||
|
|
||||||
if (parentWithAnimation === NO_PARENT_ANIMATION_ELEMENT_DETECTED) {
|
let parentHasPriority: any = null;
|
||||||
rootPlayers.push(player);
|
for (let i = 0; i < sortedParentElements.length; i++) {
|
||||||
} else {
|
const parent = sortedParentElements[i];
|
||||||
const parentPlayers = this.playersByElement.get(parentWithAnimation);
|
if (parent === element) break;
|
||||||
|
if (this.driver.containsElement(parent, element)) {
|
||||||
|
parentHasPriority = parent;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentHasPriority) {
|
||||||
|
const parentPlayers = this.playersByElement.get(parentHasPriority);
|
||||||
if (parentPlayers && parentPlayers.length) {
|
if (parentPlayers && parentPlayers.length) {
|
||||||
player.parentPlayer = optimizeGroupPlayer(parentPlayers);
|
player.parentPlayer = optimizeGroupPlayer(parentPlayers);
|
||||||
}
|
}
|
||||||
skippedPlayers.push(player);
|
skippedPlayers.push(player);
|
||||||
|
} else {
|
||||||
|
rootPlayers.push(player);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eraseStyles(element, instruction.fromStyles);
|
eraseStyles(element, instruction.fromStyles);
|
||||||
|
@ -1122,7 +1105,7 @@ export class TransitionAnimationEngine {
|
||||||
// fire the start/done transition callback events
|
// fire the start/done transition callback events
|
||||||
skippedPlayers.forEach(player => {
|
skippedPlayers.forEach(player => {
|
||||||
if (player.parentPlayer) {
|
if (player.parentPlayer) {
|
||||||
player.syncPlayerEvents(player.parentPlayer);
|
player.parentPlayer.onDestroy(() => player.destroy());
|
||||||
} else {
|
} else {
|
||||||
player.destroy();
|
player.destroy();
|
||||||
}
|
}
|
||||||
|
@ -1383,15 +1366,6 @@ export class TransitionAnimationPlayer implements AnimationPlayer {
|
||||||
|
|
||||||
getRealPlayer() { return this._player; }
|
getRealPlayer() { return this._player; }
|
||||||
|
|
||||||
syncPlayerEvents(player: AnimationPlayer) {
|
|
||||||
const p = this._player as any;
|
|
||||||
if (p.triggerCallback) {
|
|
||||||
player.onStart(() => p.triggerCallback('start'));
|
|
||||||
}
|
|
||||||
player.onDone(() => this.finish());
|
|
||||||
player.onDestroy(() => this.destroy());
|
|
||||||
}
|
|
||||||
|
|
||||||
private _queueEvent(name: string, callback: (event: any) => any): void {
|
private _queueEvent(name: string, callback: (event: any) => any): void {
|
||||||
getOrSetAsInMap(this._queuedCallbacks, name, []).push(callback);
|
getOrSetAsInMap(this._queuedCallbacks, name, []).push(callback);
|
||||||
}
|
}
|
||||||
|
@ -1445,14 +1419,6 @@ export class TransitionAnimationPlayer implements AnimationPlayer {
|
||||||
getPosition(): number { return this.queued ? 0 : this._player.getPosition(); }
|
getPosition(): number { return this.queued ? 0 : this._player.getPosition(); }
|
||||||
|
|
||||||
get totalTime(): number { return this._player.totalTime; }
|
get totalTime(): number { return this._player.totalTime; }
|
||||||
|
|
||||||
/* @internal */
|
|
||||||
triggerCallback(phaseName: string): void {
|
|
||||||
const p = this._player as any;
|
|
||||||
if (p.triggerCallback) {
|
|
||||||
p.triggerCallback(phaseName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteOrUnsetInMap(map: Map<any, any[]>| {[key: string]: any}, key: any, value: any) {
|
function deleteOrUnsetInMap(map: Map<any, any[]>| {[key: string]: any}, key: any, value: any) {
|
||||||
|
|
|
@ -184,13 +184,6 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||||
}
|
}
|
||||||
this.currentSnapshot = styles;
|
this.currentSnapshot = styles;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @internal */
|
|
||||||
triggerCallback(phaseName: string): void {
|
|
||||||
const methods = phaseName == 'start' ? this._onStartFns : this._onDoneFns;
|
|
||||||
methods.forEach(fn => fn());
|
|
||||||
methods.length = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _computeStyle(element: any, prop: string): string {
|
function _computeStyle(element: any, prop: string): string {
|
||||||
|
|
|
@ -45,26 +45,6 @@ export function main() {
|
||||||
const p = innerPlayer !;
|
const p = innerPlayer !;
|
||||||
expect(p.log).toEqual(['play']);
|
expect(p.log).toEqual(['play']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fire start/done callbacks manually when called directly', () => {
|
|
||||||
const log: string[] = [];
|
|
||||||
|
|
||||||
const player = new WebAnimationsPlayer(element, [], {duration: 1000});
|
|
||||||
player.onStart(() => log.push('started'));
|
|
||||||
player.onDone(() => log.push('done'));
|
|
||||||
|
|
||||||
player.triggerCallback('start');
|
|
||||||
expect(log).toEqual(['started']);
|
|
||||||
|
|
||||||
player.play();
|
|
||||||
expect(log).toEqual(['started']);
|
|
||||||
|
|
||||||
player.triggerCallback('done');
|
|
||||||
expect(log).toEqual(['started', 'done']);
|
|
||||||
|
|
||||||
player.finish();
|
|
||||||
expect(log).toEqual(['started', 'done']);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,11 +140,4 @@ export class AnimationGroupPlayer implements AnimationPlayer {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @internal */
|
|
||||||
triggerCallback(phaseName: string): void {
|
|
||||||
const methods = phaseName == 'start' ? this._onStartFns : this._onDoneFns;
|
|
||||||
methods.forEach(fn => fn());
|
|
||||||
methods.length = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,6 @@ export interface AnimationPlayer {
|
||||||
parentPlayer: AnimationPlayer|null;
|
parentPlayer: AnimationPlayer|null;
|
||||||
readonly totalTime: number;
|
readonly totalTime: number;
|
||||||
beforeDestroy?: () => any;
|
beforeDestroy?: () => any;
|
||||||
/* @internal */
|
|
||||||
triggerCallback?: (phaseName: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,11 +91,4 @@ export class NoopAnimationPlayer implements AnimationPlayer {
|
||||||
reset(): void {}
|
reset(): void {}
|
||||||
setPosition(p: number): void {}
|
setPosition(p: number): void {}
|
||||||
getPosition(): number { return 0; }
|
getPosition(): number { return 0; }
|
||||||
|
|
||||||
/* @internal */
|
|
||||||
triggerCallback(phaseName: string): void {
|
|
||||||
const methods = phaseName == 'start' ? this._onStartFns : this._onDoneFns;
|
|
||||||
methods.forEach(fn => fn());
|
|
||||||
methods.length = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -40,29 +40,5 @@ export function main() {
|
||||||
player.destroy();
|
player.destroy();
|
||||||
expect(log).toEqual(['started', 'done', 'destroy']);
|
expect(log).toEqual(['started', 'done', 'destroy']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fire start/done callbacks manually when called directly', fakeAsync(() => {
|
|
||||||
const log: string[] = [];
|
|
||||||
|
|
||||||
const player = new NoopAnimationPlayer();
|
|
||||||
player.onStart(() => log.push('started'));
|
|
||||||
player.onDone(() => log.push('done'));
|
|
||||||
flushMicrotasks();
|
|
||||||
|
|
||||||
player.triggerCallback('start');
|
|
||||||
expect(log).toEqual(['started']);
|
|
||||||
|
|
||||||
player.play();
|
|
||||||
expect(log).toEqual(['started']);
|
|
||||||
|
|
||||||
player.triggerCallback('done');
|
|
||||||
expect(log).toEqual(['started', 'done']);
|
|
||||||
|
|
||||||
player.finish();
|
|
||||||
expect(log).toEqual(['started', 'done']);
|
|
||||||
|
|
||||||
flushMicrotasks();
|
|
||||||
expect(log).toEqual(['started', 'done']);
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -409,39 +409,39 @@ export function main() {
|
||||||
const fixture = TestBed.createComponent(Cmp);
|
const fixture = TestBed.createComponent(Cmp);
|
||||||
const cmp = fixture.componentInstance;
|
const cmp = fixture.componentInstance;
|
||||||
|
|
||||||
|
cmp.exp0 = 0;
|
||||||
|
|
||||||
cmp.exp1 = 0;
|
cmp.exp1 = 0;
|
||||||
cmp.exp2 = 0;
|
cmp.exp2 = 0;
|
||||||
cmp.exp3 = 0;
|
cmp.exp3 = 0;
|
||||||
cmp.exp4 = 0;
|
cmp.exp4 = 0;
|
||||||
cmp.exp5 = 0;
|
cmp.exp5 = 0;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
engine.flush();
|
||||||
cmp.exp0 = 0;
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
let players = engine.players;
|
let players = engine.players;
|
||||||
cancelAllPlayers(players);
|
cancelAllPlayers(players);
|
||||||
|
|
||||||
|
cmp.exp0 = 1;
|
||||||
|
|
||||||
cmp.exp2 = 1;
|
cmp.exp2 = 1;
|
||||||
cmp.exp4 = 1;
|
cmp.exp4 = 1;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
engine.flush();
|
||||||
cmp.exp0 = 1;
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
players = engine.players;
|
players = engine.players;
|
||||||
cancelAllPlayers(players);
|
cancelAllPlayers(players);
|
||||||
expect(players.length).toEqual(3);
|
expect(players.length).toEqual(3);
|
||||||
|
|
||||||
|
cmp.exp0 = 2;
|
||||||
|
|
||||||
cmp.exp1 = 2;
|
cmp.exp1 = 2;
|
||||||
cmp.exp2 = 2;
|
cmp.exp2 = 2;
|
||||||
cmp.exp3 = 2;
|
cmp.exp3 = 2;
|
||||||
cmp.exp4 = 2;
|
cmp.exp4 = 2;
|
||||||
cmp.exp5 = 2;
|
cmp.exp5 = 2;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
engine.flush();
|
||||||
cmp.exp0 = 2;
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
players = engine.players;
|
players = engine.players;
|
||||||
cancelAllPlayers(players);
|
cancelAllPlayers(players);
|
||||||
|
@ -449,6 +449,7 @@ export function main() {
|
||||||
|
|
||||||
cmp.exp0 = 3;
|
cmp.exp0 = 3;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
engine.flush();
|
||||||
|
|
||||||
players = engine.players;
|
players = engine.players;
|
||||||
cancelAllPlayers(players);
|
cancelAllPlayers(players);
|
||||||
|
@ -2287,15 +2288,19 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]});
|
TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]});
|
||||||
|
|
||||||
|
const engine = TestBed.get(ɵAnimationEngine);
|
||||||
const fixture = TestBed.createComponent(ParentCmp);
|
const fixture = TestBed.createComponent(ParentCmp);
|
||||||
const cmp = fixture.componentInstance;
|
const cmp = fixture.componentInstance;
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
engine.flush();
|
||||||
|
|
||||||
const childCmp = cmp.childElm;
|
const childCmp = cmp.childElm;
|
||||||
|
|
||||||
cmp.exp = false;
|
cmp.exp = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
engine.flush();
|
||||||
flushMicrotasks();
|
flushMicrotasks();
|
||||||
|
|
||||||
expect(cmp.childEvent.toState).toEqual('void');
|
expect(cmp.childEvent.toState).toEqual('void');
|
||||||
|
@ -2723,106 +2728,6 @@ export function main() {
|
||||||
expect(engine.players[0].getRealPlayer()).toBe(players[1]);
|
expect(engine.players[0].getRealPlayer()).toBe(players[1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fire and synchronize the start/done callbacks on sub triggers even if they are not allowed to animate within the animation',
|
|
||||||
fakeAsync(() => {
|
|
||||||
@Component({
|
|
||||||
selector: 'parent-cmp',
|
|
||||||
animations: [
|
|
||||||
trigger(
|
|
||||||
'parent',
|
|
||||||
[
|
|
||||||
transition(
|
|
||||||
'* => go',
|
|
||||||
[
|
|
||||||
style({height: '0px'}),
|
|
||||||
animate(1000, style({height: '100px'})),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
template: `
|
|
||||||
<div *ngIf="!remove"
|
|
||||||
[@parent]="exp"
|
|
||||||
(@parent.start)="track($event)"
|
|
||||||
(@parent.done)="track($event)">
|
|
||||||
<child-cmp #child></child-cmp>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
class ParentCmp {
|
|
||||||
@ViewChild('child') public childCmp: any;
|
|
||||||
|
|
||||||
public exp: any;
|
|
||||||
public log: string[] = [];
|
|
||||||
public remove = false;
|
|
||||||
|
|
||||||
track(event: any) { this.log.push(`${event.triggerName}-${event.phaseName}`); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'child-cmp',
|
|
||||||
animations: [
|
|
||||||
trigger(
|
|
||||||
'child',
|
|
||||||
[
|
|
||||||
transition(
|
|
||||||
'* => go',
|
|
||||||
[
|
|
||||||
style({width: '0px'}),
|
|
||||||
animate(1000, style({width: '100px'})),
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
template: `
|
|
||||||
<div [@child]="exp"
|
|
||||||
(@child.start)="track($event)"
|
|
||||||
(@child.done)="track($event)"></div>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
class ChildCmp {
|
|
||||||
public exp: any;
|
|
||||||
public log: string[] = [];
|
|
||||||
track(event: any) { this.log.push(`${event.triggerName}-${event.phaseName}`); }
|
|
||||||
}
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [ParentCmp, ChildCmp]});
|
|
||||||
const engine = TestBed.get(ɵAnimationEngine);
|
|
||||||
const fixture = TestBed.createComponent(ParentCmp);
|
|
||||||
fixture.detectChanges();
|
|
||||||
flushMicrotasks();
|
|
||||||
|
|
||||||
const cmp = fixture.componentInstance;
|
|
||||||
const child = cmp.childCmp;
|
|
||||||
|
|
||||||
expect(cmp.log).toEqual(['parent-start', 'parent-done']);
|
|
||||||
expect(child.log).toEqual(['child-start', 'child-done']);
|
|
||||||
|
|
||||||
cmp.log = [];
|
|
||||||
child.log = [];
|
|
||||||
cmp.exp = 'go';
|
|
||||||
cmp.childCmp.exp = 'go';
|
|
||||||
fixture.detectChanges();
|
|
||||||
flushMicrotasks();
|
|
||||||
|
|
||||||
expect(cmp.log).toEqual(['parent-start']);
|
|
||||||
expect(child.log).toEqual(['child-start']);
|
|
||||||
|
|
||||||
const players = engine.players;
|
|
||||||
expect(players.length).toEqual(1);
|
|
||||||
players[0].finish();
|
|
||||||
|
|
||||||
expect(cmp.log).toEqual(['parent-start', 'parent-done']);
|
|
||||||
expect(child.log).toEqual(['child-start', 'child-done']);
|
|
||||||
|
|
||||||
cmp.log = [];
|
|
||||||
child.log = [];
|
|
||||||
cmp.remove = true;
|
|
||||||
fixture.detectChanges();
|
|
||||||
flushMicrotasks();
|
|
||||||
|
|
||||||
expect(cmp.log).toEqual(['parent-start', 'parent-done']);
|
|
||||||
expect(child.log).toEqual(['child-start', 'child-done']);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should stretch the starting keyframe of a child animation queries are issued by the parent',
|
it('should stretch the starting keyframe of a child animation queries are issued by the parent',
|
||||||
() => {
|
() => {
|
||||||
@Component({
|
@Component({
|
||||||
|
|
Loading…
Reference in New Issue