From 3c561475c83ed3116c6452afc2aa990ce2c0ac7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Mon, 22 Aug 2016 16:39:52 -0700 Subject: [PATCH] refactor(animations): add an onStart handler for AnimationPlayer (#10360) --- .../src/animation/animation_group_player.ts | 17 ++++++++++----- .../core/src/animation/animation_player.ts | 21 +++++++++++++------ .../animation/animation_sequence_player.ts | 17 ++++++++++----- .../animation/animation_group_player_spec.ts | 12 +++++++++++ .../test/animation/animation_player_spec.ts | 12 +++++++++++ .../animation_sequence_player_spec.ts | 12 +++++++++++ .../core/testing/mock_animation_player.ts | 21 ++++++++++++------- .../src/dom/web_animations_player.ts | 16 ++++++++++---- .../test/dom/web_animations_player_spec.ts | 11 ++++++++++ tools/public_api_guard/core/index.d.ts | 3 ++- 10 files changed, 114 insertions(+), 28 deletions(-) diff --git a/modules/@angular/core/src/animation/animation_group_player.ts b/modules/@angular/core/src/animation/animation_group_player.ts index 8782f2d0ba..eda304b49f 100644 --- a/modules/@angular/core/src/animation/animation_group_player.ts +++ b/modules/@angular/core/src/animation/animation_group_player.ts @@ -12,7 +12,8 @@ import {Math} from '../facade/math'; import {AnimationPlayer} from './animation_player'; export class AnimationGroupPlayer implements AnimationPlayer { - private _subscriptions: Function[] = []; + private _onDoneFns: Function[] = []; + private _onStartFns: Function[] = []; private _finished = false; private _started = false; @@ -41,14 +42,16 @@ export class AnimationGroupPlayer implements AnimationPlayer { if (!isPresent(this.parentPlayer)) { this.destroy(); } - this._subscriptions.forEach(subscription => subscription()); - this._subscriptions = []; + this._onDoneFns.forEach(fn => fn()); + this._onDoneFns = []; } } init(): void { this._players.forEach(player => player.init()); } - onDone(fn: Function): void { this._subscriptions.push(fn); } + onStart(fn: () => void): void { this._onStartFns.push(fn); } + + onDone(fn: () => void): void { this._onDoneFns.push(fn); } hasStarted() { return this._started; } @@ -56,7 +59,11 @@ export class AnimationGroupPlayer implements AnimationPlayer { if (!isPresent(this.parentPlayer)) { this.init(); } - this._started = true; + if (!this.hasStarted()) { + this._onStartFns.forEach(fn => fn()); + this._onStartFns = []; + this._started = true; + } this._players.forEach(player => player.play()); } diff --git a/modules/@angular/core/src/animation/animation_player.ts b/modules/@angular/core/src/animation/animation_player.ts index 1aab6b3143..6de219fd01 100644 --- a/modules/@angular/core/src/animation/animation_player.ts +++ b/modules/@angular/core/src/animation/animation_player.ts @@ -14,7 +14,8 @@ import {scheduleMicroTask} from '../facade/lang'; * @experimental Animation support is experimental. */ export abstract class AnimationPlayer { - abstract onDone(fn: Function): void; + abstract onDone(fn: () => void): void; + abstract onStart(fn: () => void): void; abstract init(): void; abstract hasStarted(): boolean; abstract play(): void; @@ -32,19 +33,27 @@ export abstract class AnimationPlayer { } export class NoOpAnimationPlayer implements AnimationPlayer { - private _subscriptions: any[] /** TODO #9100 */ = []; + private _onDoneFns: Function[] = []; + private _onStartFns: Function[] = []; private _started = false; public parentPlayer: AnimationPlayer = null; constructor() { scheduleMicroTask(() => this._onFinish()); } /** @internal */ _onFinish() { - this._subscriptions.forEach(entry => { entry(); }); - this._subscriptions = []; + this._onDoneFns.forEach(fn => fn()); + this._onDoneFns = []; } - onDone(fn: Function): void { this._subscriptions.push(fn); } + onStart(fn: () => void): void { this._onStartFns.push(fn); } + onDone(fn: () => void): void { this._onDoneFns.push(fn); } hasStarted(): boolean { return this._started; } init(): void {} - play(): void { this._started = true; } + play(): void { + if (!this.hasStarted()) { + this._onStartFns.forEach(fn => fn()); + this._onStartFns = []; + } + this._started = true; + } pause(): void {} restart(): void {} finish(): void { this._onFinish(); } diff --git a/modules/@angular/core/src/animation/animation_sequence_player.ts b/modules/@angular/core/src/animation/animation_sequence_player.ts index d8e9415e57..28becdf9bb 100644 --- a/modules/@angular/core/src/animation/animation_sequence_player.ts +++ b/modules/@angular/core/src/animation/animation_sequence_player.ts @@ -13,7 +13,8 @@ import {AnimationPlayer, NoOpAnimationPlayer} from './animation_player'; export class AnimationSequencePlayer implements AnimationPlayer { private _currentIndex: number = 0; private _activePlayer: AnimationPlayer; - private _subscriptions: Function[] = []; + private _onDoneFns: Function[] = []; + private _onStartFns: Function[] = []; private _finished = false; private _started: boolean = false; @@ -50,14 +51,16 @@ export class AnimationSequencePlayer implements AnimationPlayer { if (!isPresent(this.parentPlayer)) { this.destroy(); } - this._subscriptions.forEach(subscription => subscription()); - this._subscriptions = []; + this._onDoneFns.forEach(fn => fn()); + this._onDoneFns = []; } } init(): void { this._players.forEach(player => player.init()); } - onDone(fn: Function): void { this._subscriptions.push(fn); } + onStart(fn: () => void): void { this._onStartFns.push(fn); } + + onDone(fn: () => void): void { this._onDoneFns.push(fn); } hasStarted() { return this._started; } @@ -65,7 +68,11 @@ export class AnimationSequencePlayer implements AnimationPlayer { if (!isPresent(this.parentPlayer)) { this.init(); } - this._started = true; + if (!this.hasStarted()) { + this._onStartFns.forEach(fn => fn()); + this._onStartFns = []; + this._started = true; + } this._activePlayer.play(); } diff --git a/modules/@angular/core/test/animation/animation_group_player_spec.ts b/modules/@angular/core/test/animation/animation_group_player_spec.ts index 8d320a07b3..8bc8ff8074 100644 --- a/modules/@angular/core/test/animation/animation_group_player_spec.ts +++ b/modules/@angular/core/test/animation/animation_group_player_spec.ts @@ -173,6 +173,18 @@ export function main() { group.destroy(); }); + it('should run the onStart method when started but only once', () => { + var player = new AnimationGroupPlayer([]); + var calls = 0; + player.onStart(() => calls++); + expect(calls).toEqual(0); + player.play(); + expect(calls).toEqual(1); + player.pause(); + player.play(); + expect(calls).toEqual(1); + }); + it('should call onDone after the next microtask if no players are provided', fakeAsync(() => { var group = new AnimationGroupPlayer([]); var completed = false; diff --git a/modules/@angular/core/test/animation/animation_player_spec.ts b/modules/@angular/core/test/animation/animation_player_spec.ts index a1d4c46687..227ff6e7e2 100644 --- a/modules/@angular/core/test/animation/animation_player_spec.ts +++ b/modules/@angular/core/test/animation/animation_player_spec.ts @@ -29,5 +29,17 @@ export function main() { player.restart(); player.destroy(); })); + + it('should run the onStart method when started but only once', fakeAsync(() => { + var player = new NoOpAnimationPlayer(); + var calls = 0; + player.onStart(() => calls++); + expect(calls).toEqual(0); + player.play(); + expect(calls).toEqual(1); + player.pause(); + player.play(); + expect(calls).toEqual(1); + })); }); } diff --git a/modules/@angular/core/test/animation/animation_sequence_player_spec.ts b/modules/@angular/core/test/animation/animation_sequence_player_spec.ts index f266d669b2..841b61792f 100644 --- a/modules/@angular/core/test/animation/animation_sequence_player_spec.ts +++ b/modules/@angular/core/test/animation/animation_sequence_player_spec.ts @@ -196,6 +196,18 @@ export function main() { sequence.destroy(); }); + it('should run the onStart method when started but only once', () => { + var player = new AnimationSequencePlayer([]); + var calls = 0; + player.onStart(() => calls++); + expect(calls).toEqual(0); + player.play(); + expect(calls).toEqual(1); + player.pause(); + player.play(); + expect(calls).toEqual(1); + }); + it('should call onDone after the next microtask if no players are provided', fakeAsync(() => { var sequence = new AnimationSequencePlayer([]); var completed = false; diff --git a/modules/@angular/core/testing/mock_animation_player.ts b/modules/@angular/core/testing/mock_animation_player.ts index fe1bdf69d8..2e7e51f334 100644 --- a/modules/@angular/core/testing/mock_animation_player.ts +++ b/modules/@angular/core/testing/mock_animation_player.ts @@ -10,7 +10,8 @@ import {AnimationPlayer} from '../src/animation/animation_player'; import {isPresent} from '../src/facade/lang'; export class MockAnimationPlayer implements AnimationPlayer { - private _subscriptions: any[] /** TODO #9100 */ = []; + private _onDoneFns: Function[] = []; + private _onStartFns: Function[] = []; private _finished = false; private _destroyed = false; private _started: boolean = false; @@ -19,13 +20,13 @@ export class MockAnimationPlayer implements AnimationPlayer { public log: any[] /** TODO #9100 */ = []; - private _onfinish(): void { + private _onFinish(): void { if (!this._finished) { this._finished = true; this.log.push('finish'); - this._subscriptions.forEach((entry) => { entry(); }); - this._subscriptions = []; + this._onDoneFns.forEach(fn => fn()); + this._onDoneFns = []; if (!isPresent(this.parentPlayer)) { this.destroy(); } @@ -34,12 +35,18 @@ export class MockAnimationPlayer implements AnimationPlayer { init(): void { this.log.push('init'); } - onDone(fn: Function): void { this._subscriptions.push(fn); } + onDone(fn: () => void): void { this._onDoneFns.push(fn); } + + onStart(fn: () => void): void { this._onStartFns.push(fn); } hasStarted() { return this._started; } play(): void { - this._started = true; + if (!this.hasStarted()) { + this._onStartFns.forEach(fn => fn()); + this._onStartFns = []; + this._started = true; + } this.log.push('play'); } @@ -47,7 +54,7 @@ export class MockAnimationPlayer implements AnimationPlayer { restart(): void { this.log.push('restart'); } - finish(): void { this._onfinish(); } + finish(): void { this._onFinish(); } reset(): void { this.log.push('reset'); } diff --git a/modules/@angular/platform-browser/src/dom/web_animations_player.ts b/modules/@angular/platform-browser/src/dom/web_animations_player.ts index d58b1bd2dc..fdb0b6f98b 100644 --- a/modules/@angular/platform-browser/src/dom/web_animations_player.ts +++ b/modules/@angular/platform-browser/src/dom/web_animations_player.ts @@ -16,7 +16,8 @@ import {getDOM} from './dom_adapter'; import {DomAnimatePlayer} from './dom_animate_player'; export class WebAnimationsPlayer implements AnimationPlayer { - private _subscriptions: Function[] = []; + private _onDoneFns: Function[] = []; + private _onStartFns: Function[] = []; private _finished = false; private _initialized = false; private _player: DomAnimatePlayer; @@ -37,8 +38,8 @@ export class WebAnimationsPlayer implements AnimationPlayer { if (!isPresent(this.parentPlayer)) { this.destroy(); } - this._subscriptions.forEach(fn => fn()); - this._subscriptions = []; + this._onDoneFns.forEach(fn => fn()); + this._onDoneFns = []; } } @@ -66,10 +67,17 @@ export class WebAnimationsPlayer implements AnimationPlayer { return element.animate(keyframes, options); } - onDone(fn: Function): void { this._subscriptions.push(fn); } + onStart(fn: () => void): void { this._onStartFns.push(fn); } + + onDone(fn: () => void): void { this._onDoneFns.push(fn); } play(): void { this.init(); + if (!this.hasStarted()) { + this._onStartFns.forEach(fn => fn()); + this._onStartFns = []; + this._started = true; + } this._player.play(); } diff --git a/modules/@angular/platform-browser/test/dom/web_animations_player_spec.ts b/modules/@angular/platform-browser/test/dom/web_animations_player_spec.ts index c83656cda7..5a98a46b7d 100644 --- a/modules/@angular/platform-browser/test/dom/web_animations_player_spec.ts +++ b/modules/@angular/platform-browser/test/dom/web_animations_player_spec.ts @@ -128,5 +128,16 @@ export function main() { expect(captures2['finish'].length).toEqual(1); expect(captures2['cancel'].length).toEqual(0); }); + + it('should run the onStart method when started but only once', () => { + var calls = 0; + player.onStart(() => calls++); + expect(calls).toEqual(0); + player.play(); + expect(calls).toEqual(1); + player.pause(); + player.play(); + expect(calls).toEqual(1); + }); }); } diff --git a/tools/public_api_guard/core/index.d.ts b/tools/public_api_guard/core/index.d.ts index f5e43719bd..38552dc9e3 100644 --- a/tools/public_api_guard/core/index.d.ts +++ b/tools/public_api_guard/core/index.d.ts @@ -69,7 +69,8 @@ export declare abstract class AnimationPlayer { abstract getPosition(): number; abstract hasStarted(): boolean; abstract init(): void; - abstract onDone(fn: Function): void; + abstract onDone(fn: () => void): void; + abstract onStart(fn: () => void): void; abstract pause(): void; abstract play(): void; abstract reset(): void;