fix(animations): avoid infinite loop with multiple blocked sub triggers (#21119)
This patch fixes animations so that if multiple sub @triggers are used and are blocked by a parent animation then the engine will not lead itself into an infinite loop. PR Close #21119
This commit is contained in:
parent
5ba1cf1063
commit
86a36eaadd
|
@ -33,17 +33,17 @@ export class AnimationGroupPlayer implements AnimationPlayer {
|
||||||
} else {
|
} else {
|
||||||
this.players.forEach(player => {
|
this.players.forEach(player => {
|
||||||
player.onDone(() => {
|
player.onDone(() => {
|
||||||
if (++doneCount >= total) {
|
if (++doneCount == total) {
|
||||||
this._onFinish();
|
this._onFinish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
player.onDestroy(() => {
|
player.onDestroy(() => {
|
||||||
if (++destroyCount >= total) {
|
if (++destroyCount == total) {
|
||||||
this._onDestroy();
|
this._onDestroy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
player.onStart(() => {
|
player.onStart(() => {
|
||||||
if (++startCount >= total) {
|
if (++startCount == total) {
|
||||||
this._onStart();
|
this._onStart();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -67,9 +67,9 @@ export class AnimationGroupPlayer implements AnimationPlayer {
|
||||||
|
|
||||||
private _onStart() {
|
private _onStart() {
|
||||||
if (!this.hasStarted()) {
|
if (!this.hasStarted()) {
|
||||||
|
this._started = true;
|
||||||
this._onStartFns.forEach(fn => fn());
|
this._onStartFns.forEach(fn => fn());
|
||||||
this._onStartFns = [];
|
this._onStartFns = [];
|
||||||
this._started = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2823,6 +2823,96 @@ export function main() {
|
||||||
expect(child.log).toEqual(['child-start', 'child-done']);
|
expect(child.log).toEqual(['child-start', 'child-done']);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should fire and synchronize the start/done callbacks on multiple blocked sub triggers',
|
||||||
|
fakeAsync(() => {
|
||||||
|
@Component({
|
||||||
|
selector: 'cmp',
|
||||||
|
animations: [
|
||||||
|
trigger(
|
||||||
|
'parent1',
|
||||||
|
[
|
||||||
|
transition(
|
||||||
|
'* => go, * => go-again',
|
||||||
|
[
|
||||||
|
style({opacity: 0}),
|
||||||
|
animate('1s', style({opacity: 1})),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
trigger(
|
||||||
|
'parent2',
|
||||||
|
[
|
||||||
|
transition(
|
||||||
|
'* => go, * => go-again',
|
||||||
|
[
|
||||||
|
style({lineHeight: '0px'}),
|
||||||
|
animate('1s', style({lineHeight: '10px'})),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
trigger(
|
||||||
|
'child1',
|
||||||
|
[
|
||||||
|
transition(
|
||||||
|
'* => go, * => go-again',
|
||||||
|
[
|
||||||
|
style({width: '0px'}),
|
||||||
|
animate('1s', style({width: '100px'})),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
trigger(
|
||||||
|
'child2',
|
||||||
|
[
|
||||||
|
transition(
|
||||||
|
'* => go, * => go-again',
|
||||||
|
[
|
||||||
|
style({height: '0px'}),
|
||||||
|
animate('1s', style({height: '100px'})),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
template: `
|
||||||
|
<div [@parent1]="parent1Exp" (@parent1.start)="track($event)"
|
||||||
|
[@parent2]="parent2Exp" (@parent2.start)="track($event)">
|
||||||
|
<div [@child1]="child1Exp" (@child1.start)="track($event)"
|
||||||
|
[@child2]="child2Exp" (@child2.start)="track($event)"></div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class Cmp {
|
||||||
|
public parent1Exp = '';
|
||||||
|
public parent2Exp = '';
|
||||||
|
public child1Exp = '';
|
||||||
|
public child2Exp = '';
|
||||||
|
public log: string[] = [];
|
||||||
|
|
||||||
|
track(event: any) { this.log.push(`${event.triggerName}-${event.phaseName}`); }
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||||
|
const engine = TestBed.get(ɵAnimationEngine);
|
||||||
|
const fixture = TestBed.createComponent(Cmp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
flushMicrotasks();
|
||||||
|
|
||||||
|
const cmp = fixture.componentInstance;
|
||||||
|
cmp.log = [];
|
||||||
|
cmp.parent1Exp = 'go';
|
||||||
|
cmp.parent2Exp = 'go';
|
||||||
|
cmp.child1Exp = 'go';
|
||||||
|
cmp.child2Exp = 'go';
|
||||||
|
fixture.detectChanges();
|
||||||
|
flushMicrotasks();
|
||||||
|
|
||||||
|
expect(cmp.log).toEqual(
|
||||||
|
['parent1-start', 'parent2-start', 'child1-start', 'child2-start']);
|
||||||
|
|
||||||
|
cmp.parent1Exp = 'go-again';
|
||||||
|
cmp.parent2Exp = 'go-again';
|
||||||
|
cmp.child1Exp = 'go-again';
|
||||||
|
cmp.child2Exp = 'go-again';
|
||||||
|
fixture.detectChanges();
|
||||||
|
flushMicrotasks();
|
||||||
|
}));
|
||||||
|
|
||||||
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