fix(animations): ensure all child elements are rendered before running animations

Closes #9402
Closes #9775
This commit is contained in:
Matias Niemelä
2016-07-01 16:01:57 -07:00
parent 7073cf74fe
commit cbe85a0893
20 changed files with 283 additions and 81 deletions
@@ -14,6 +14,8 @@ import {AnimationPlayer} from './animation_player';
export class AnimationGroupPlayer implements AnimationPlayer {
private _subscriptions: Function[] = [];
private _finished = false;
private _started = false;
public parentPlayer: AnimationPlayer = null;
constructor(private _players: AnimationPlayer[]) {
@@ -44,9 +46,19 @@ export class AnimationGroupPlayer implements AnimationPlayer {
}
}
init(): void { this._players.forEach(player => player.init()); }
onDone(fn: Function): void { this._subscriptions.push(fn); }
play() { this._players.forEach(player => player.play()); }
hasStarted() { return this._started; }
play() {
if (!isPresent(this.parentPlayer)) {
this.init();
}
this._started = true;
this._players.forEach(player => player.play());
}
pause(): void { this._players.forEach(player => player.pause()); }
@@ -15,6 +15,8 @@ import {scheduleMicroTask} from '../facade/lang';
*/
export abstract class AnimationPlayer {
abstract onDone(fn: Function): void;
abstract init(): void;
abstract hasStarted(): boolean;
abstract play(): void;
abstract pause(): void;
abstract restart(): void;
@@ -31,6 +33,7 @@ export abstract class AnimationPlayer {
export class NoOpAnimationPlayer implements AnimationPlayer {
private _subscriptions: any[] /** TODO #9100 */ = [];
private _started = false;
public parentPlayer: AnimationPlayer = null;
constructor() { scheduleMicroTask(() => this._onFinish()); }
/** @internal */
@@ -39,7 +42,9 @@ export class NoOpAnimationPlayer implements AnimationPlayer {
this._subscriptions = [];
}
onDone(fn: Function): void { this._subscriptions.push(fn); }
play(): void {}
hasStarted(): boolean { return this._started; }
init(): void {}
play(): void { this._started = true; }
pause(): void {}
restart(): void {}
finish(): void { this._onFinish(); }
@@ -15,6 +15,7 @@ export class AnimationSequencePlayer implements AnimationPlayer {
private _activePlayer: AnimationPlayer;
private _subscriptions: Function[] = [];
private _finished = false;
private _started: boolean = false;
public parentPlayer: AnimationPlayer = null;
@@ -54,9 +55,19 @@ export class AnimationSequencePlayer implements AnimationPlayer {
}
}
init(): void { this._players.forEach(player => player.init()); }
onDone(fn: Function): void { this._subscriptions.push(fn); }
play(): void { this._activePlayer.play(); }
hasStarted() { return this._started; }
play(): void {
if (!isPresent(this.parentPlayer)) {
this.init();
}
this._started = true;
this._activePlayer.play();
}
pause(): void { this._activePlayer.pause(); }
@@ -11,7 +11,7 @@ import {isPresent} from '../facade/lang';
import {AnimationPlayer} from './animation_player';
export class ActiveAnimationPlayersMap {
export class ViewAnimationMap {
private _map = new Map<any, {[key: string]: AnimationPlayer}>();
private _allPlayers: AnimationPlayer[] = [];
@@ -25,9 +25,9 @@ export class ActiveAnimationPlayersMap {
}
findAllPlayersByElement(element: any): AnimationPlayer[] {
var players: any[] /** TODO #9100 */ = [];
var players: AnimationPlayer[] = [];
StringMapWrapper.forEach(
this._map.get(element), (player: any /** TODO #9100 */) => players.push(player));
this._map.get(element), (player: AnimationPlayer) => players.push(player));
return players;
}
+19 -13
View File
@@ -28,7 +28,7 @@ import {AnimationPlayer} from '../animation/animation_player';
import {AnimationGroupPlayer} from '../animation/animation_group_player';
import {AnimationKeyframe} from '../animation/animation_keyframe';
import {AnimationStyles} from '../animation/animation_styles';
import {ActiveAnimationPlayersMap} from '../animation/active_animation_players_map';
import {ViewAnimationMap} from '../animation/view_animation_map';
var _scope_check: WtfScopeFn = wtfCreateScope(`AppView#check(ascii id)`);
@@ -54,7 +54,7 @@ export abstract class AppView<T> {
private _hasExternalHostElement: boolean;
public activeAnimationPlayers = new ActiveAnimationPlayersMap();
public animationPlayers = new ViewAnimationMap();
public context: T;
@@ -74,20 +74,26 @@ export abstract class AppView<T> {
cancelActiveAnimation(element: any, animationName: string, removeAllAnimations: boolean = false) {
if (removeAllAnimations) {
this.activeAnimationPlayers.findAllPlayersByElement(element).forEach(
player => player.destroy());
this.animationPlayers.findAllPlayersByElement(element).forEach(player => player.destroy());
} else {
var player = this.activeAnimationPlayers.find(element, animationName);
var player = this.animationPlayers.find(element, animationName);
if (isPresent(player)) {
player.destroy();
}
}
}
registerAndStartAnimation(element: any, animationName: string, player: AnimationPlayer): void {
this.activeAnimationPlayers.set(element, animationName, player);
player.onDone(() => { this.activeAnimationPlayers.remove(element, animationName); });
player.play();
queueAnimation(element: any, animationName: string, player: AnimationPlayer): void {
this.animationPlayers.set(element, animationName, player);
player.onDone(() => { this.animationPlayers.remove(element, animationName); });
}
triggerQueuedAnimations() {
this.animationPlayers.getAllPlayers().forEach(player => {
if (!player.hasStarted()) {
player.play();
}
});
}
create(context: T, givenProjectableNodes: Array<any|any[]>, rootSelectorOrNode: string|any):
@@ -201,10 +207,10 @@ export abstract class AppView<T> {
this.destroyInternal();
this.dirtyParentQueriesInternal();
if (this.activeAnimationPlayers.length == 0) {
if (this.animationPlayers.length == 0) {
this.renderer.destroyView(hostElement, this.allNodes);
} else {
var player = new AnimationGroupPlayer(this.activeAnimationPlayers.getAllPlayers());
var player = new AnimationGroupPlayer(this.animationPlayers.getAllPlayers());
player.onDone(() => { this.renderer.destroyView(hostElement, this.allNodes); });
}
}
@@ -221,10 +227,10 @@ export abstract class AppView<T> {
detach(): void {
this.detachInternal();
if (this.activeAnimationPlayers.length == 0) {
if (this.animationPlayers.length == 0) {
this.renderer.detachView(this.flatRootNodes);
} else {
var player = new AnimationGroupPlayer(this.activeAnimationPlayers.getAllPlayers());
var player = new AnimationGroupPlayer(this.animationPlayers.getAllPlayers());
player.onDone(() => { this.renderer.detachView(this.flatRootNodes); });
}
}