Skip to content

Commit 23146c9

Browse files
matskoalxhub
authored andcommitted
fix(animations): capture cancelled animation styles within grouped animations
Closes #17170
1 parent a5205c6 commit 23146c9

File tree

3 files changed

+99
-5
lines changed

3 files changed

+99
-5
lines changed

packages/animations/browser/src/render/transition_animation_engine.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import {AUTO_STYLE, AnimationOptions, AnimationPlayer, NoopAnimationPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations';
8+
import {AUTO_STYLE, AnimationOptions, AnimationPlayer, NoopAnimationPlayer, ɵAnimationGroupPlayer as AnimationGroupPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations';
99

1010
import {AnimationTimelineInstruction} from '../dsl/animation_timeline_instruction';
1111
import {AnimationTransitionFactory} from '../dsl/animation_transition_factory';
@@ -1168,8 +1168,8 @@ export class TransitionAnimationEngine {
11681168
if (details && details.removedBeforeQueried) return new NoopAnimationPlayer();
11691169

11701170
const isQueriedElement = element !== rootElement;
1171-
const previousPlayers =
1172-
(allPreviousPlayersMap.get(element) || EMPTY_PLAYER_ARRAY).map(p => p.getRealPlayer());
1171+
const previousPlayers = flattenGroupPlayers(
1172+
(allPreviousPlayersMap.get(element) || EMPTY_PLAYER_ARRAY).map(p => p.getRealPlayer()));
11731173

11741174
const preStyles = preStylesMap.get(element);
11751175
const postStyles = postStylesMap.get(element);
@@ -1464,3 +1464,20 @@ function removeNodesAfterAnimationDone(
14641464
engine: TransitionAnimationEngine, element: any, players: AnimationPlayer[]) {
14651465
optimizeGroupPlayer(players).onDone(() => engine.processLeaveNode(element));
14661466
}
1467+
1468+
function flattenGroupPlayers(players: AnimationPlayer[]): AnimationPlayer[] {
1469+
const finalPlayers: AnimationPlayer[] = [];
1470+
_flattenGroupPlayersRecur(players, finalPlayers);
1471+
return finalPlayers;
1472+
}
1473+
1474+
function _flattenGroupPlayersRecur(players: AnimationPlayer[], finalPlayers: AnimationPlayer[]) {
1475+
for (let i = 0; i < players.length; i++) {
1476+
const player = players[i];
1477+
if (player instanceof AnimationGroupPlayer) {
1478+
_flattenGroupPlayersRecur(player.players, finalPlayers);
1479+
} else {
1480+
finalPlayers.push(player as AnimationPlayer);
1481+
}
1482+
}
1483+
}

packages/animations/src/players/animation_group_player.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,12 @@ export class AnimationGroupPlayer implements AnimationPlayer {
132132
}
133133

134134
get players(): AnimationPlayer[] { return this._players; }
135+
136+
beforeDestroy(): void {
137+
this.players.forEach(player => {
138+
if (player.beforeDestroy) {
139+
player.beforeDestroy();
140+
}
141+
});
142+
}
135143
}

packages/core/test/animation/animation_integration_spec.ts

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,75 @@ export function main() {
799799
expect(p3.previousStyles).toEqual({});
800800
});
801801

802+
it('should provide the styling of previous players that are grouped', () => {
803+
@Component({
804+
selector: 'ani-cmp',
805+
template: `
806+
<div [@myAnimation]="exp"></div>
807+
`,
808+
animations: [trigger(
809+
'myAnimation',
810+
[
811+
transition(
812+
'1 => 2',
813+
[
814+
group([
815+
animate(500, style({'width': '100px'})),
816+
animate(500, style({'height': '100px'})),
817+
]),
818+
animate(500, keyframes([
819+
style({'opacity': '0'}),
820+
style({'opacity': '1'})
821+
]))
822+
]),
823+
transition(
824+
'2 => 3',
825+
[
826+
style({'opacity': '0'}),
827+
animate(500, style({'opacity': '1'})),
828+
]),
829+
])],
830+
})
831+
class Cmp {
832+
exp: any = false;
833+
}
834+
835+
TestBed.configureTestingModule({declarations: [Cmp]});
836+
837+
const engine = TestBed.get(ɵAnimationEngine);
838+
const fixture = TestBed.createComponent(Cmp);
839+
const cmp = fixture.componentInstance;
840+
841+
fixture.detectChanges();
842+
engine.flush();
843+
844+
cmp.exp = '1';
845+
fixture.detectChanges();
846+
engine.flush();
847+
expect(getLog().length).toEqual(0);
848+
resetLog();
849+
850+
cmp.exp = '2';
851+
fixture.detectChanges();
852+
engine.flush();
853+
expect(getLog().length).toEqual(3);
854+
resetLog();
855+
856+
cmp.exp = '3';
857+
fixture.detectChanges();
858+
engine.flush();
859+
860+
const players = getLog();
861+
expect(players.length).toEqual(1);
862+
const player = players[0] as MockAnimationPlayer;
863+
const pp = player.previousPlayers as MockAnimationPlayer[];
864+
865+
expect(pp.length).toEqual(3);
866+
expect(pp[0].currentSnapshot).toEqual({width: AUTO_STYLE});
867+
expect(pp[1].currentSnapshot).toEqual({height: AUTO_STYLE});
868+
expect(pp[2].currentSnapshot).toEqual({opacity: AUTO_STYLE});
869+
});
870+
802871
it('should properly balance styles between states even if there are no destination state styles',
803872
() => {
804873
@Component({
@@ -1815,12 +1884,12 @@ export function main() {
18151884
selector: 'my-cmp',
18161885
template: `
18171886
<div class="parent" [@parent]="exp" (@parent.done)="cb('all','done', $event)">
1818-
<div *ngFor="let item of items"
1887+
<div *ngFor="let item of items"
18191888
class="item item-{{ item }}"
18201889
@child
18211890
(@child.start)="cb('c-' + item, 'start', $event)"
18221891
(@child.done)="cb('c-' + item, 'done', $event)">
1823-
{{ item }}
1892+
{{ item }}
18241893
</div>
18251894
</div>
18261895
`,

0 commit comments

Comments
 (0)