fix(animations): make sure non-transitioned leave operations cancel existing animations (#15254)
Closes #15213 PR Close #15254
This commit is contained in:
parent
d0bc83ca27
commit
a6fb78ee3c
@ -28,7 +28,8 @@ export interface TriggerListenerTuple {
|
|||||||
callback: (event: any) => any;
|
callback: (event: any) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MARKED_FOR_ANIMATION = 'ng-animate';
|
const MARKED_FOR_ANIMATION_CLASSNAME = 'ng-animating';
|
||||||
|
const MARKED_FOR_ANIMATION_SELECTOR = '.ng-animating';
|
||||||
const MARKED_FOR_REMOVAL = '$$ngRemove';
|
const MARKED_FOR_REMOVAL = '$$ngRemove';
|
||||||
|
|
||||||
export class DomAnimationEngine {
|
export class DomAnimationEngine {
|
||||||
@ -92,6 +93,7 @@ export class DomAnimationEngine {
|
|||||||
element[MARKED_FOR_REMOVAL] = true;
|
element[MARKED_FOR_REMOVAL] = true;
|
||||||
this._queuedRemovals.set(element, () => {});
|
this._queuedRemovals.set(element, () => {});
|
||||||
}
|
}
|
||||||
|
this._onRemovalTransition(element).forEach(player => player.destroy());
|
||||||
domFn();
|
domFn();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +164,7 @@ export class DomAnimationEngine {
|
|||||||
// when a parent animation is set to trigger a removal we want to
|
// when a parent animation is set to trigger a removal we want to
|
||||||
// find all of the children that are currently animating and clear
|
// find all of the children that are currently animating and clear
|
||||||
// them out by destroying each of them.
|
// them out by destroying each of them.
|
||||||
const elms = element.querySelectorAll(MARKED_FOR_ANIMATION);
|
const elms = element.querySelectorAll(MARKED_FOR_ANIMATION_SELECTOR);
|
||||||
for (let i = 0; i < elms.length; i++) {
|
for (let i = 0; i < elms.length; i++) {
|
||||||
const elm = elms[i];
|
const elm = elms[i];
|
||||||
const activePlayers = this._activeElementAnimations.get(elm);
|
const activePlayers = this._activeElementAnimations.get(elm);
|
||||||
@ -300,8 +302,8 @@ export class DomAnimationEngine {
|
|||||||
this._queuedTransitionAnimations.push(tuple);
|
this._queuedTransitionAnimations.push(tuple);
|
||||||
player.init();
|
player.init();
|
||||||
|
|
||||||
element.classList.add(MARKED_FOR_ANIMATION);
|
element.classList.add(MARKED_FOR_ANIMATION_CLASSNAME);
|
||||||
player.onDone(() => { element.classList.remove(MARKED_FOR_ANIMATION); });
|
player.onDone(() => { element.classList.remove(MARKED_FOR_ANIMATION_CLASSNAME); });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _flushQueuedAnimations() {
|
private _flushQueuedAnimations() {
|
||||||
|
@ -366,6 +366,51 @@ export function main() {
|
|||||||
assertHasParent(element, false);
|
assertHasParent(element, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should properly cancel all existing animations when a removal occurs', () => {
|
||||||
|
@Component({
|
||||||
|
selector: 'ani-cmp',
|
||||||
|
template: `
|
||||||
|
<div *ngIf="exp" [@myAnimation]="exp"></div>
|
||||||
|
`,
|
||||||
|
animations: [
|
||||||
|
trigger(
|
||||||
|
'myAnimation',
|
||||||
|
[
|
||||||
|
transition(
|
||||||
|
'* => go', [style({width: '0px'}), animate(1000, style({width: '100px'}))]),
|
||||||
|
]),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class Cmp {
|
||||||
|
public exp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||||
|
|
||||||
|
const engine = TestBed.get(ɵAnimationEngine);
|
||||||
|
const fixture = TestBed.createComponent(Cmp);
|
||||||
|
const cmp = fixture.componentInstance;
|
||||||
|
cmp.exp = 'go';
|
||||||
|
fixture.detectChanges();
|
||||||
|
engine.flush();
|
||||||
|
expect(getLog().length).toEqual(1);
|
||||||
|
const [player1] = getLog();
|
||||||
|
resetLog();
|
||||||
|
|
||||||
|
let finished = false;
|
||||||
|
player1.onDone(() => finished = true);
|
||||||
|
|
||||||
|
let destroyed = false;
|
||||||
|
player1.onDestroy(() => destroyed = true);
|
||||||
|
|
||||||
|
cmp.exp = null;
|
||||||
|
fixture.detectChanges();
|
||||||
|
engine.flush();
|
||||||
|
|
||||||
|
expect(finished).toBeTruthy();
|
||||||
|
expect(destroyed).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
it('should not run inner child animations when a parent is set to be removed', () => {
|
it('should not run inner child animations when a parent is set to be removed', () => {
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ani-cmp',
|
selector: 'ani-cmp',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user