Skip to content

Commit e7d9fd8

Browse files
matskochuckjaz
authored andcommitted
fix(animations): repair flicker issues with WA polyfill (#16937)
Fixes #16919 Fixes #16918
1 parent 08dfe91 commit e7d9fd8

File tree

7 files changed

+85
-13
lines changed

7 files changed

+85
-13
lines changed

packages/animations/browser/src/dsl/style_normalization/web_animations_style_normalizer.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
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 {dashCaseToCamelCase} from '../../util';
9+
810
import {AnimationStyleNormalizer} from './animation_style_normalizer';
911

1012
export class WebAnimationsStyleNormalizer extends AnimationStyleNormalizer {
@@ -41,8 +43,3 @@ function makeBooleanMap(keys: string[]): {[key: string]: boolean} {
4143
keys.forEach(key => map[key] = true);
4244
return map;
4345
}
44-
45-
const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
46-
export function dashCaseToCamelCase(input: string): string {
47-
return input.replace(DASH_CASE_REGEXP, (...m: any[]) => m[1].toUpperCase());
48-
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -997,9 +997,8 @@ export class TransitionAnimationEngine {
997997
});
998998

999999
allConsumedElements.forEach(element => addClass(element, NG_ANIMATING_CLASSNAME));
1000-
10011000
const player = optimizeGroupPlayer(allNewPlayers);
1002-
player.onDone(() => {
1001+
player.onDestroy(() => {
10031002
allConsumedElements.forEach(element => removeClass(element, NG_ANIMATING_CLASSNAME));
10041003
setStyles(rootElement, instruction.toStyles);
10051004
});

packages/animations/browser/src/render/web_animations/web_animations_player.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ export class WebAnimationsPlayer implements AnimationPlayer {
5353
}
5454

5555
init(): void {
56+
this._buildPlayer();
57+
this._preparePlayerBeforeStart();
58+
}
59+
60+
private _buildPlayer(): void {
5661
if (this._initialized) return;
5762
this._initialized = true;
5863

@@ -82,14 +87,16 @@ export class WebAnimationsPlayer implements AnimationPlayer {
8287

8388
this._player = this._triggerWebAnimation(this.element, keyframes, this.options);
8489
this._finalKeyframe = keyframes.length ? keyframes[keyframes.length - 1] : {};
90+
this._player.addEventListener('finish', () => this._onFinish());
91+
}
8592

93+
private _preparePlayerBeforeStart() {
8694
// this is required so that the player doesn't start to animate right away
8795
if (this._delay) {
8896
this._resetDomPlayerState();
8997
} else {
9098
this._player.pause();
9199
}
92-
this._player.addEventListener('finish', () => this._onFinish());
93100
}
94101

95102
/** @internal */
@@ -108,7 +115,7 @@ export class WebAnimationsPlayer implements AnimationPlayer {
108115
onDestroy(fn: () => void): void { this._onDestroyFns.push(fn); }
109116

110117
play(): void {
111-
this.init();
118+
this._buildPlayer();
112119
if (!this.hasStarted()) {
113120
this._onStartFns.forEach(fn => fn());
114121
this._onStartFns = [];

packages/animations/browser/src/util.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,18 @@ export function copyStyles(
123123

124124
export function setStyles(element: any, styles: ɵStyleData) {
125125
if (element['style']) {
126-
Object.keys(styles).forEach(prop => element.style[prop] = styles[prop]);
126+
Object.keys(styles).forEach(prop => {
127+
const camelProp = dashCaseToCamelCase(prop);
128+
element.style[camelProp] = styles[prop];
129+
});
127130
}
128131
}
129132

130133
export function eraseStyles(element: any, styles: ɵStyleData) {
131134
if (element['style']) {
132135
Object.keys(styles).forEach(prop => {
133-
// IE requires '' instead of null
134-
// see https://github.com/angular/angular/issues/7916
135-
element.style[prop] = '';
136+
const camelProp = dashCaseToCamelCase(prop);
137+
element.style[camelProp] = '';
136138
});
137139
}
138140
}
@@ -206,3 +208,8 @@ export function mergeAnimationOptions(
206208
}
207209
return destination;
208210
}
211+
212+
const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
213+
export function dashCaseToCamelCase(input: string): string {
214+
return input.replace(DASH_CASE_REGEXP, (...m: any[]) => m[1].toUpperCase());
215+
}

packages/animations/browser/test/engine/timeline_animation_engine_spec.ts renamed to packages/animations/browser/test/render/timeline_animation_engine_spec.ts

File renamed without changes.

packages/animations/browser/test/engine/transition_animation_engine_spec.ts renamed to packages/animations/browser/test/render/transition_animation_engine_spec.ts

File renamed without changes.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import {DOMAnimation} from '../../../src/render/web_animations/dom_animation';
9+
import {WebAnimationsPlayer} from '../../../src/render/web_animations/web_animations_player';
10+
11+
export function main() {
12+
let element: any;
13+
let innerPlayer: MockDomAnimation|null = null;
14+
beforeEach(() => {
15+
element = {};
16+
element['animate'] = () => { return innerPlayer = new MockDomAnimation(); };
17+
});
18+
19+
describe('WebAnimationsPlayer tests', () => {
20+
it('should automatically pause the player when created and initialized', () => {
21+
const keyframes = [
22+
{opacity: 0, offset: 0},
23+
{opacity: 1, offset: 1},
24+
];
25+
26+
const player = new WebAnimationsPlayer(element, keyframes, {duration: 1000});
27+
28+
player.init();
29+
const p = innerPlayer !;
30+
expect(p.log).toEqual(['pause']);
31+
32+
player.play();
33+
expect(p.log).toEqual(['pause', 'play']);
34+
});
35+
36+
it('should not pause the player if created and started before initialized', () => {
37+
const keyframes = [
38+
{opacity: 0, offset: 0},
39+
{opacity: 1, offset: 1},
40+
];
41+
42+
const player = new WebAnimationsPlayer(element, keyframes, {duration: 1000});
43+
44+
player.play();
45+
const p = innerPlayer !;
46+
expect(p.log).toEqual(['play']);
47+
});
48+
});
49+
}
50+
51+
class MockDomAnimation implements DOMAnimation {
52+
log: string[] = [];
53+
cancel(): void { this.log.push('cancel'); }
54+
play(): void { this.log.push('play'); }
55+
pause(): void { this.log.push('pause'); }
56+
finish(): void { this.log.push('finish'); }
57+
onfinish: Function = () => {};
58+
position: number = 0;
59+
currentTime: number = 0;
60+
addEventListener(eventName: string, handler: (event: any) => any): any {}
61+
dispatchEvent(eventName: string): any {}
62+
}

0 commit comments

Comments
 (0)