@ -40,7 +40,11 @@ export {
|
||||
wtfEndTimeRange,
|
||||
WtfScopeFn
|
||||
} from './src/profile/profile';
|
||||
|
||||
export {Type, enableProdMode} from "./src/facade/lang";
|
||||
export {EventEmitter} from "./src/facade/async";
|
||||
export {ExceptionHandler, WrappedException, BaseException} from "./src/facade/exceptions";
|
||||
export * from './private_export';
|
||||
|
||||
export * from './src/animation/metadata';
|
||||
export {AnimationPlayer} from './src/animation/animation_player';
|
||||
|
@ -21,6 +21,27 @@ import * as provider_util from './src/di/provider_util';
|
||||
import * as console from './src/console';
|
||||
import {Provider} from './index';
|
||||
|
||||
import {
|
||||
NoOpAnimationPlayer as NoOpAnimationPlayer_,
|
||||
AnimationPlayer as AnimationPlayer_
|
||||
} from './src/animation/animation_player';
|
||||
import {
|
||||
NoOpAnimationDriver as NoOpAnimationDriver_,
|
||||
AnimationDriver as AnimationDriver_
|
||||
} from './src/animation/animation_driver';
|
||||
import {AnimationSequencePlayer as AnimationSequencePlayer_} from './src/animation/animation_sequence_player';
|
||||
import {AnimationGroupPlayer as AnimationGroupPlayer_} from './src/animation/animation_group_player';
|
||||
import {AnimationKeyframe as AnimationKeyframe_} from './src/animation/animation_keyframe';
|
||||
import {AnimationStyleUtil as AnimationStyleUtil_} from './src/animation/animation_style_util';
|
||||
import {AnimationStyles as AnimationStyles_} from './src/animation/animation_styles';
|
||||
import {
|
||||
ANY_STATE as ANY_STATE_,
|
||||
EMPTY_STATE as EMPTY_STATE_,
|
||||
FILL_STYLE_FLAG as FILL_STYLE_FLAG_
|
||||
} from './src/animation/animation_constants';
|
||||
import {MockAnimationPlayer as MockAnimationPlayer_} from './testing/animation/mock_animation_player';
|
||||
import {MockAnimationDriver as MockAnimationDriver_} from './testing/animation/mock_animation_driver';
|
||||
|
||||
export declare namespace __core_private_types__ {
|
||||
export var isDefaultChangeDetectionStrategy: typeof constants.isDefaultChangeDetectionStrategy;
|
||||
export type ChangeDetectorState = constants.ChangeDetectorState;
|
||||
@ -82,6 +103,31 @@ export declare namespace __core_private_types__ {
|
||||
export var castByValue: typeof view_utils.castByValue;
|
||||
export type Console = console.Console;
|
||||
export var Console: typeof console.Console;
|
||||
export type NoOpAnimationPlayer = NoOpAnimationPlayer_;
|
||||
export var NoOpAnimationPlayer: typeof NoOpAnimationPlayer_;
|
||||
export type AnimationPlayer = AnimationPlayer_;
|
||||
export var AnimationPlayer: typeof AnimationPlayer_;
|
||||
export type NoOpAnimationDriver = NoOpAnimationDriver_;
|
||||
export var NoOpAnimationDriver: typeof NoOpAnimationDriver_;
|
||||
export type AnimationDriver = AnimationDriver_;
|
||||
export var AnimationDriver: typeof AnimationDriver_;
|
||||
export type AnimationSequencePlayer = AnimationSequencePlayer_;
|
||||
export var AnimationSequencePlayer: typeof AnimationSequencePlayer_;
|
||||
export type AnimationGroupPlayer = AnimationGroupPlayer_;
|
||||
export var AnimationGroupPlayer: typeof AnimationGroupPlayer_;
|
||||
export type AnimationKeyframe = AnimationKeyframe_;
|
||||
export var AnimationKeyframe: typeof AnimationKeyframe_;
|
||||
export type AnimationStyleUtil = AnimationStyleUtil_;
|
||||
export var AnimationStyleUtil: typeof AnimationStyleUtil_;
|
||||
export type AnimationStyles = AnimationStyles_;
|
||||
export var AnimationStyles: typeof AnimationStyles_;
|
||||
export var ANY_STATE: typeof ANY_STATE_;
|
||||
export var EMPTY_STATE: typeof EMPTY_STATE_;
|
||||
export var FILL_STYLE_FLAG: typeof FILL_STYLE_FLAG_;
|
||||
export type MockAnimationPlayer = MockAnimationPlayer_;
|
||||
export var MockAnimationPlayer: typeof MockAnimationPlayer_;
|
||||
export type MockAnimationDriver = MockAnimationDriver_;
|
||||
export var MockAnimationDriver: typeof MockAnimationDriver_;
|
||||
}
|
||||
|
||||
export var __core_private__ = {
|
||||
@ -132,4 +178,18 @@ export var __core_private__ = {
|
||||
pureProxy10: view_utils.pureProxy10,
|
||||
castByValue: view_utils.castByValue,
|
||||
Console: console.Console,
|
||||
NoOpAnimationPlayer: NoOpAnimationPlayer_,
|
||||
AnimationPlayer: AnimationPlayer_,
|
||||
NoOpAnimationDriver: NoOpAnimationDriver_,
|
||||
AnimationDriver: AnimationDriver_,
|
||||
AnimationSequencePlayer: AnimationSequencePlayer_,
|
||||
AnimationGroupPlayer: AnimationGroupPlayer_,
|
||||
AnimationKeyframe: AnimationKeyframe_,
|
||||
AnimationStyleUtil: AnimationStyleUtil_,
|
||||
AnimationStyles: AnimationStyles_,
|
||||
MockAnimationPlayer: MockAnimationPlayer_,
|
||||
MockAnimationDriver: MockAnimationDriver_,
|
||||
ANY_STATE: ANY_STATE_,
|
||||
EMPTY_STATE: EMPTY_STATE_,
|
||||
FILL_STYLE_FLAG: FILL_STYLE_FLAG_
|
||||
};
|
||||
|
@ -0,0 +1,57 @@
|
||||
import {AnimationPlayer} from './animation_player';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {ListWrapper, StringMapWrapper, Map} from '../facade/collection';
|
||||
|
||||
export class ActiveAnimationPlayersMap {
|
||||
private _map = new Map<any, {[key: string]: AnimationPlayer}>();
|
||||
private _allPlayers: AnimationPlayer[] = [];
|
||||
|
||||
get length(): number {
|
||||
return this.getAllPlayers().length;
|
||||
}
|
||||
|
||||
find(element: any, animationName: string): AnimationPlayer {
|
||||
var playersByAnimation = this._map.get(element);
|
||||
if (isPresent(playersByAnimation)) {
|
||||
return playersByAnimation[animationName];
|
||||
}
|
||||
}
|
||||
|
||||
findAllPlayersByElement(element: any): AnimationPlayer[] {
|
||||
var players = [];
|
||||
StringMapWrapper.forEach(this._map.get(element), player => players.push(player));
|
||||
return players;
|
||||
}
|
||||
|
||||
set(element: any, animationName: string, player: AnimationPlayer): void {
|
||||
var playersByAnimation = this._map.get(element);
|
||||
if (!isPresent(playersByAnimation)) {
|
||||
playersByAnimation = {};
|
||||
}
|
||||
var existingEntry = playersByAnimation[animationName];
|
||||
if (isPresent(existingEntry)) {
|
||||
this.remove(element, animationName);
|
||||
}
|
||||
playersByAnimation[animationName] = player;
|
||||
this._allPlayers.push(player);
|
||||
this._map.set(element, playersByAnimation);
|
||||
}
|
||||
|
||||
getAllPlayers(): AnimationPlayer[] {
|
||||
return this._allPlayers;
|
||||
}
|
||||
|
||||
remove(element: any, animationName: string): void {
|
||||
var playersByAnimation = this._map.get(element);
|
||||
if (isPresent(playersByAnimation)) {
|
||||
var player = playersByAnimation[animationName];
|
||||
delete playersByAnimation[animationName];
|
||||
var index = this._allPlayers.indexOf(player);
|
||||
ListWrapper.removeAt(this._allPlayers, index);
|
||||
|
||||
if (StringMapWrapper.isEmpty(playersByAnimation)) {
|
||||
this._map.delete(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
export const FILL_STYLE_FLAG = 'true'; // TODO (matsko): change to boolean
|
||||
export const ANY_STATE = '*';
|
||||
export const EMPTY_STATE = 'void';
|
15
modules/@angular/core/src/animation/animation_driver.ts
Normal file
15
modules/@angular/core/src/animation/animation_driver.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import {NoOpAnimationPlayer, AnimationPlayer} from './animation_player';
|
||||
import {AnimationKeyframe} from './animation_keyframe';
|
||||
import {AnimationStyles} from './animation_styles';
|
||||
|
||||
export abstract class AnimationDriver {
|
||||
abstract animate(element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[], duration: number, delay: number,
|
||||
easing: string): AnimationPlayer;
|
||||
}
|
||||
|
||||
export class NoOpAnimationDriver extends AnimationDriver {
|
||||
animate(element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[], duration: number, delay: number,
|
||||
easing: string): AnimationPlayer {
|
||||
return new NoOpAnimationPlayer();
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
import {AnimationPlayer} from './animation_player';
|
||||
import {isPresent, scheduleMicroTask} from '../facade/lang';
|
||||
import {Math} from '../facade/math';
|
||||
|
||||
export class AnimationGroupPlayer implements AnimationPlayer {
|
||||
private _subscriptions: Function[] = [];
|
||||
private _finished = false;
|
||||
public parentPlayer: AnimationPlayer = null;
|
||||
|
||||
constructor(private _players: AnimationPlayer[]) {
|
||||
var count = 0;
|
||||
var total = this._players.length;
|
||||
if (total == 0) {
|
||||
scheduleMicroTask(() => this._onFinish());
|
||||
} else {
|
||||
this._players.forEach(player => {
|
||||
player.parentPlayer = this;
|
||||
player.onDone(() => {
|
||||
if (++count >= total) {
|
||||
this._onFinish();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _onFinish() {
|
||||
if (!this._finished) {
|
||||
this._finished = true;
|
||||
if (!isPresent(this.parentPlayer)) {
|
||||
this.destroy();
|
||||
}
|
||||
this._subscriptions.forEach(subscription => subscription());
|
||||
this._subscriptions = [];
|
||||
}
|
||||
}
|
||||
|
||||
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
||||
|
||||
play() { this._players.forEach(player => player.play()); }
|
||||
|
||||
pause(): void { this._players.forEach(player => player.pause()); }
|
||||
|
||||
restart(): void { this._players.forEach(player => player.restart()); }
|
||||
|
||||
finish(): void {
|
||||
this._onFinish();
|
||||
this._players.forEach(player => player.finish());
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this._onFinish();
|
||||
this._players.forEach(player => player.destroy());
|
||||
}
|
||||
|
||||
reset(): void { this._players.forEach(player => player.reset()); }
|
||||
|
||||
setPosition(p): void {
|
||||
this._players.forEach(player => {
|
||||
player.setPosition(p);
|
||||
});
|
||||
}
|
||||
|
||||
getPosition(): number {
|
||||
var min = 0;
|
||||
this._players.forEach(player => {
|
||||
var p = player.getPosition();
|
||||
min = Math.min(p, min);
|
||||
});
|
||||
return min;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
import {AnimationStyles} from './animation_styles';
|
||||
|
||||
export class AnimationKeyframe {
|
||||
constructor(public offset: number, public styles: AnimationStyles) {}
|
||||
}
|
39
modules/@angular/core/src/animation/animation_player.ts
Normal file
39
modules/@angular/core/src/animation/animation_player.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import {scheduleMicroTask} from '../facade/lang';
|
||||
import {BaseException} from '../facade/exceptions';
|
||||
|
||||
export abstract class AnimationPlayer {
|
||||
abstract onDone(fn: Function): void;
|
||||
abstract play(): void;
|
||||
abstract pause(): void;
|
||||
abstract restart(): void;
|
||||
abstract finish(): void;
|
||||
abstract destroy(): void;
|
||||
abstract reset(): void;
|
||||
abstract setPosition(p): void;
|
||||
abstract getPosition(): number;
|
||||
get parentPlayer(): AnimationPlayer { throw new BaseException('NOT IMPLEMENTED: Base Class'); }
|
||||
set parentPlayer(player: AnimationPlayer) { throw new BaseException('NOT IMPLEMENTED: Base Class'); }
|
||||
}
|
||||
|
||||
export class NoOpAnimationPlayer implements AnimationPlayer {
|
||||
private _subscriptions = [];
|
||||
public parentPlayer: AnimationPlayer = null;
|
||||
constructor() {
|
||||
scheduleMicroTask(() => this._onFinish());
|
||||
}
|
||||
_onFinish() {
|
||||
this._subscriptions.forEach(entry => { entry(); });
|
||||
this._subscriptions = [];
|
||||
}
|
||||
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
||||
play(): void {}
|
||||
pause(): void {}
|
||||
restart(): void {}
|
||||
finish(): void {
|
||||
this._onFinish();
|
||||
}
|
||||
destroy(): void {}
|
||||
reset(): void {}
|
||||
setPosition(p): void {}
|
||||
getPosition(): number { return 0; }
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {NoOpAnimationPlayer, AnimationPlayer} from './animation_player';
|
||||
import {scheduleMicroTask} from '../facade/lang';
|
||||
|
||||
export class AnimationSequencePlayer implements AnimationPlayer {
|
||||
private _currentIndex: number = 0;
|
||||
private _activePlayer: AnimationPlayer;
|
||||
private _subscriptions: Function[] = [];
|
||||
private _finished = false;
|
||||
|
||||
public parentPlayer: AnimationPlayer = null;
|
||||
|
||||
constructor(private _players: AnimationPlayer[]) {
|
||||
this._players.forEach(player => {
|
||||
player.parentPlayer = this;
|
||||
});
|
||||
this._onNext(false);
|
||||
}
|
||||
|
||||
private _onNext(start: boolean) {
|
||||
if (this._finished) return;
|
||||
|
||||
if (this._players.length == 0) {
|
||||
this._activePlayer = new NoOpAnimationPlayer();
|
||||
scheduleMicroTask(() => this._onFinish());
|
||||
} else if (this._currentIndex >= this._players.length) {
|
||||
this._activePlayer = new NoOpAnimationPlayer();
|
||||
this._onFinish();
|
||||
} else {
|
||||
var player = this._players[this._currentIndex++];
|
||||
player.onDone(() => this._onNext(true));
|
||||
|
||||
this._activePlayer = player;
|
||||
if (start) {
|
||||
player.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _onFinish() {
|
||||
if (!this._finished) {
|
||||
this._finished = true;
|
||||
if (!isPresent(this.parentPlayer)) {
|
||||
this.destroy();
|
||||
}
|
||||
this._subscriptions.forEach(subscription => subscription());
|
||||
this._subscriptions = [];
|
||||
}
|
||||
}
|
||||
|
||||
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
||||
|
||||
play(): void { this._activePlayer.play(); }
|
||||
|
||||
pause(): void { this._activePlayer.pause(); }
|
||||
|
||||
restart(): void {
|
||||
if (this._players.length > 0) {
|
||||
this.reset();
|
||||
this._players[0].restart();
|
||||
}
|
||||
}
|
||||
|
||||
reset(): void { this._players.forEach(player => player.reset()); }
|
||||
|
||||
finish(): void {
|
||||
this._onFinish();
|
||||
this._players.forEach(player => player.finish());
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this._onFinish();
|
||||
this._players.forEach(player => player.destroy());
|
||||
}
|
||||
|
||||
setPosition(p): void {
|
||||
this._players[0].setPosition(p);
|
||||
}
|
||||
|
||||
getPosition(): number {
|
||||
return this._players[0].getPosition();
|
||||
}
|
||||
}
|
113
modules/@angular/core/src/animation/animation_style_util.ts
Normal file
113
modules/@angular/core/src/animation/animation_style_util.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import {isPresent, isArray} from '../facade/lang';
|
||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
||||
import {AUTO_STYLE} from './metadata';
|
||||
import {FILL_STYLE_FLAG} from './animation_constants';
|
||||
|
||||
export class AnimationStyleUtil {
|
||||
static balanceStyles(previousStyles: {[key: string]: string|number},
|
||||
newStyles: {[key: string]: string|number},
|
||||
nullValue = null): {[key: string]: string|number} {
|
||||
var finalStyles: {[key: string]: string|number} = {};
|
||||
|
||||
StringMapWrapper.forEach(newStyles, (value, prop) => {
|
||||
finalStyles[prop] = value;
|
||||
});
|
||||
|
||||
StringMapWrapper.forEach(previousStyles, (value, prop) => {
|
||||
if (!isPresent(finalStyles[prop])) {
|
||||
finalStyles[prop] = nullValue;
|
||||
}
|
||||
});
|
||||
|
||||
return finalStyles;
|
||||
}
|
||||
static balanceKeyframes(collectedStyles: {[key: string]: string|number},
|
||||
finalStateStyles: {[key: string]: string|number},
|
||||
keyframes: any[]): any[] {
|
||||
var limit = keyframes.length - 1;
|
||||
var firstKeyframe = keyframes[0];
|
||||
|
||||
// phase 1: copy all the styles from the first keyframe into the lookup map
|
||||
var flatenedFirstKeyframeStyles = AnimationStyleUtil.flattenStyles(firstKeyframe.styles.styles);
|
||||
|
||||
var extraFirstKeyframeStyles = {};
|
||||
var hasExtraFirstStyles = false;
|
||||
StringMapWrapper.forEach(collectedStyles, (value, prop) => {
|
||||
// if the style is already defined in the first keyframe then
|
||||
// we do not replace it.
|
||||
if (!flatenedFirstKeyframeStyles[prop]) {
|
||||
flatenedFirstKeyframeStyles[prop] = value;
|
||||
extraFirstKeyframeStyles[prop] = value;
|
||||
hasExtraFirstStyles = true;
|
||||
}
|
||||
});
|
||||
|
||||
var keyframeCollectedStyles = StringMapWrapper.merge({}, flatenedFirstKeyframeStyles);
|
||||
|
||||
// phase 2: normalize the final keyframe
|
||||
var finalKeyframe = keyframes[limit];
|
||||
ListWrapper.insert(finalKeyframe.styles.styles, 0, finalStateStyles);
|
||||
|
||||
var flatenedFinalKeyframeStyles = AnimationStyleUtil.flattenStyles(finalKeyframe.styles.styles);
|
||||
var extraFinalKeyframeStyles = {};
|
||||
var hasExtraFinalStyles = false;
|
||||
StringMapWrapper.forEach(keyframeCollectedStyles, (value, prop) => {
|
||||
if (!isPresent(flatenedFinalKeyframeStyles[prop])) {
|
||||
extraFinalKeyframeStyles[prop] = AUTO_STYLE;
|
||||
hasExtraFinalStyles = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (hasExtraFinalStyles) {
|
||||
finalKeyframe.styles.styles.push(extraFinalKeyframeStyles);
|
||||
}
|
||||
|
||||
StringMapWrapper.forEach(flatenedFinalKeyframeStyles, (value, prop) => {
|
||||
if (!isPresent(flatenedFirstKeyframeStyles[prop])) {
|
||||
extraFirstKeyframeStyles[prop] = AUTO_STYLE;
|
||||
hasExtraFirstStyles = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (hasExtraFirstStyles) {
|
||||
firstKeyframe.styles.styles.push(extraFirstKeyframeStyles);
|
||||
}
|
||||
|
||||
return keyframes;
|
||||
}
|
||||
|
||||
static clearStyles(styles: {[key: string]: string|number}): {[key: string]: string|number} {
|
||||
var finalStyles: {[key: string]: string|number} = {};
|
||||
StringMapWrapper.keys(styles).forEach(key => {
|
||||
finalStyles[key] = null;
|
||||
});
|
||||
return finalStyles;
|
||||
}
|
||||
|
||||
static collectAndResolveStyles(collection: {[key: string]: string|number}, styles: {[key: string]: string|number}[]) {
|
||||
return styles.map(entry => {
|
||||
var stylesObj = {};
|
||||
StringMapWrapper.forEach(entry, (value, prop) => {
|
||||
if (value == FILL_STYLE_FLAG) {
|
||||
value = collection[prop];
|
||||
if (!isPresent(value)) {
|
||||
value = AUTO_STYLE;
|
||||
}
|
||||
}
|
||||
collection[prop] = value;
|
||||
stylesObj[prop] = value;
|
||||
});
|
||||
return stylesObj;
|
||||
});
|
||||
}
|
||||
|
||||
static flattenStyles(styles: {[key: string]: string|number}[]) {
|
||||
var finalStyles = {};
|
||||
styles.forEach(entry => {
|
||||
StringMapWrapper.forEach(entry, (value, prop) => {
|
||||
finalStyles[prop] = value;
|
||||
});
|
||||
});
|
||||
return finalStyles;
|
||||
}
|
||||
}
|
3
modules/@angular/core/src/animation/animation_styles.ts
Normal file
3
modules/@angular/core/src/animation/animation_styles.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export class AnimationStyles {
|
||||
constructor(public styles: {[key: string]: string | number}[]) {}
|
||||
}
|
116
modules/@angular/core/src/animation/metadata.ts
Normal file
116
modules/@angular/core/src/animation/metadata.ts
Normal file
@ -0,0 +1,116 @@
|
||||
import {isPresent, isArray, isString, isStringMap, NumberWrapper} from '../facade/lang';
|
||||
import {BaseException} from '../facade/exceptions';
|
||||
|
||||
export const AUTO_STYLE = "*";
|
||||
|
||||
export class AnimationEntryMetadata {
|
||||
constructor(public name: string, public definitions: AnimationStateMetadata[]) {}
|
||||
}
|
||||
|
||||
export abstract class AnimationStateMetadata {}
|
||||
|
||||
export class AnimationStateDeclarationMetadata extends AnimationStateMetadata {
|
||||
constructor(public stateNameExpr: string, public styles: AnimationStyleMetadata) { super(); }
|
||||
}
|
||||
|
||||
export class AnimationStateTransitionMetadata extends AnimationStateMetadata {
|
||||
constructor(public stateChangeExpr: string, public animation: AnimationMetadata) { super(); }
|
||||
}
|
||||
|
||||
export abstract class AnimationMetadata {}
|
||||
|
||||
export class AnimationKeyframesSequenceMetadata extends AnimationMetadata {
|
||||
constructor(public steps: AnimationStyleMetadata[]) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
export class AnimationStyleMetadata extends AnimationMetadata {
|
||||
constructor(public styles: Array<string|{[key: string]: string | number}>, public offset: number = null) { super(); }
|
||||
}
|
||||
|
||||
export class AnimationAnimateMetadata extends AnimationMetadata {
|
||||
constructor(public timings: string | number,
|
||||
public styles: AnimationStyleMetadata|AnimationKeyframesSequenceMetadata) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class AnimationWithStepsMetadata extends AnimationMetadata {
|
||||
constructor() { super(); }
|
||||
get steps(): AnimationMetadata[] { throw new BaseException('NOT IMPLEMENTED: Base Class'); }
|
||||
}
|
||||
|
||||
export class AnimationSequenceMetadata extends AnimationWithStepsMetadata {
|
||||
constructor(private _steps: AnimationMetadata[]) { super(); }
|
||||
get steps(): AnimationMetadata[] { return this._steps; }
|
||||
}
|
||||
|
||||
export class AnimationGroupMetadata extends AnimationWithStepsMetadata {
|
||||
constructor(private _steps: AnimationMetadata[]) { super(); }
|
||||
get steps(): AnimationMetadata[] { return this._steps; }
|
||||
}
|
||||
|
||||
export function animate(timing: string | number,
|
||||
styles: AnimationStyleMetadata|AnimationKeyframesSequenceMetadata = null): AnimationAnimateMetadata {
|
||||
var stylesEntry = styles;
|
||||
if (!isPresent(stylesEntry)) {
|
||||
var EMPTY_STYLE: {[key: string]: string|number} = {};
|
||||
stylesEntry = new AnimationStyleMetadata([EMPTY_STYLE], 1);
|
||||
}
|
||||
return new AnimationAnimateMetadata(timing, stylesEntry);
|
||||
}
|
||||
|
||||
export function group(steps: AnimationMetadata[]): AnimationGroupMetadata {
|
||||
return new AnimationGroupMetadata(steps);
|
||||
}
|
||||
|
||||
export function sequence(steps: AnimationMetadata[]): AnimationSequenceMetadata {
|
||||
return new AnimationSequenceMetadata(steps);
|
||||
}
|
||||
|
||||
export function style(tokens: string|{[key: string]: string | number}|Array<string|{[key: string]: string | number}>): AnimationStyleMetadata {
|
||||
var input: Array<{[key: string]: string | number}|string>;
|
||||
var offset: number = null;
|
||||
if (isString(tokens)) {
|
||||
input = [<string>tokens];
|
||||
} else {
|
||||
if (isArray(tokens)) {
|
||||
input = <Array<{[key: string]: string | number}>>tokens;
|
||||
} else {
|
||||
input = [<{[key: string]: string | number}>tokens];
|
||||
}
|
||||
input.forEach(entry => {
|
||||
var entryOffset = entry['offset'];
|
||||
if (isPresent(entryOffset)) {
|
||||
offset = offset == null ? NumberWrapper.parseFloat(entryOffset) : offset;
|
||||
}
|
||||
});
|
||||
}
|
||||
return new AnimationStyleMetadata(input, offset);
|
||||
}
|
||||
|
||||
export function state(stateNameExpr: string, styles: AnimationStyleMetadata): AnimationStateDeclarationMetadata {
|
||||
return new AnimationStateDeclarationMetadata(stateNameExpr, styles);
|
||||
}
|
||||
|
||||
export function keyframes(steps: AnimationStyleMetadata|AnimationStyleMetadata[]): AnimationKeyframesSequenceMetadata {
|
||||
var stepData = isArray(steps)
|
||||
? <AnimationStyleMetadata[]>steps
|
||||
: [<AnimationStyleMetadata>steps];
|
||||
return new AnimationKeyframesSequenceMetadata(stepData);
|
||||
}
|
||||
|
||||
export function transition(stateChangeExpr: string, animationData: AnimationMetadata|AnimationMetadata[]): AnimationStateTransitionMetadata {
|
||||
var animation = isArray(animationData)
|
||||
? new AnimationSequenceMetadata(<AnimationMetadata[]>animationData)
|
||||
: <AnimationMetadata>animationData;
|
||||
return new AnimationStateTransitionMetadata(stateChangeExpr, animation);
|
||||
}
|
||||
|
||||
export function trigger(name: string, animation: AnimationMetadata|AnimationMetadata[]): AnimationEntryMetadata {
|
||||
var entry = isArray(animation)
|
||||
? <AnimationMetadata[]>animation
|
||||
: [<AnimationMetadata>animation];
|
||||
return new AnimationEntryMetadata(name, entry);
|
||||
}
|
@ -9,6 +9,10 @@ import {
|
||||
removeDebugNodeFromIndex
|
||||
} from './debug_node';
|
||||
|
||||
import {AnimationKeyframe} from '../animation/animation_keyframe';
|
||||
import {AnimationStyles} from '../animation/animation_styles';
|
||||
import {AnimationPlayer} from '../animation/animation_player';
|
||||
|
||||
export class DebugDomRootRenderer implements RootRenderer {
|
||||
constructor(private _delegate: RootRenderer) {}
|
||||
|
||||
@ -137,4 +141,8 @@ export class DebugDomRenderer implements Renderer {
|
||||
}
|
||||
|
||||
setText(renderNode: any, text: string) { this._delegate.setText(renderNode, text); }
|
||||
|
||||
animate(element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[], duration: number, delay: number, easing: string): AnimationPlayer {
|
||||
return this._delegate.animate(element, startingStyles, keyframes, duration, delay, easing);
|
||||
}
|
||||
}
|
||||
|
@ -81,8 +81,7 @@ export class AppElement {
|
||||
if (view.type === ViewType.COMPONENT) {
|
||||
throw new BaseException(`Component views can't be moved!`);
|
||||
}
|
||||
|
||||
view.renderer.detachView(view.flatRootNodes);
|
||||
view.detach();
|
||||
|
||||
view.removeFromContentChildren(this);
|
||||
return view;
|
||||
|
@ -1,7 +1,9 @@
|
||||
import {
|
||||
ListWrapper,
|
||||
StringMapWrapper,
|
||||
} from '../../src/facade/collection';
|
||||
Map,
|
||||
MapWrapper
|
||||
} from '../facade/collection';
|
||||
|
||||
import {AppElement} from './element';
|
||||
import {
|
||||
@ -14,9 +16,9 @@ import {
|
||||
stringify,
|
||||
isPrimitive,
|
||||
isString
|
||||
} from '../../src/facade/lang';
|
||||
} from '../facade/lang';
|
||||
|
||||
import {ObservableWrapper} from '../../src/facade/async';
|
||||
import {ObservableWrapper} from '../facade/async';
|
||||
import {Renderer, RootRenderer, RenderComponentType, RenderDebugInfo} from '../render/api';
|
||||
import {ViewRef_} from './view_ref';
|
||||
|
||||
@ -43,6 +45,14 @@ import {StaticNodeDebugInfo, DebugContext} from './debug_context';
|
||||
import {ElementInjector} from './element_injector';
|
||||
import {Injector} from '../di/injector';
|
||||
|
||||
import {AUTO_STYLE} from '../animation/metadata';
|
||||
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 {AnimationDriver} from '../animation/animation_driver';
|
||||
import {ActiveAnimationPlayersMap} from '../animation/active_animation_players_map';
|
||||
|
||||
var _scope_check: WtfScopeFn = wtfCreateScope(`AppView#check(ascii id)`);
|
||||
|
||||
/**
|
||||
@ -71,6 +81,8 @@ export abstract class AppView<T> {
|
||||
|
||||
private _hasExternalHostElement: boolean;
|
||||
|
||||
public activeAnimationPlayers = new ActiveAnimationPlayersMap();
|
||||
|
||||
public context: T;
|
||||
|
||||
constructor(public clazz: any, public componentType: RenderComponentType, public type: ViewType,
|
||||
@ -84,6 +96,25 @@ export abstract class AppView<T> {
|
||||
}
|
||||
}
|
||||
|
||||
cancelActiveAnimation(element: any, animationName: string, removeAllAnimations: boolean = false) {
|
||||
if (removeAllAnimations) {
|
||||
this.activeAnimationPlayers.findAllPlayersByElement(element).forEach(player => player.destroy());
|
||||
} else {
|
||||
var player = this.activeAnimationPlayers.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();
|
||||
}
|
||||
|
||||
create(context: T, givenProjectableNodes: Array<any | any[]>,
|
||||
rootSelectorOrNode: string | any): AppElement {
|
||||
this.context = context;
|
||||
@ -193,7 +224,15 @@ export abstract class AppView<T> {
|
||||
}
|
||||
this.destroyInternal();
|
||||
this.dirtyParentQueriesInternal();
|
||||
this.renderer.destroyView(hostElement, this.allNodes);
|
||||
|
||||
if (this.activeAnimationPlayers.length == 0) {
|
||||
this.renderer.destroyView(hostElement, this.allNodes);
|
||||
} else {
|
||||
var player = new AnimationGroupPlayer(this.activeAnimationPlayers.getAllPlayers());
|
||||
player.onDone(() => {
|
||||
this.renderer.destroyView(hostElement, this.allNodes);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -201,6 +240,23 @@ export abstract class AppView<T> {
|
||||
*/
|
||||
destroyInternal(): void {}
|
||||
|
||||
/**
|
||||
* Overwritten by implementations
|
||||
*/
|
||||
detachInternal(): void {}
|
||||
|
||||
detach(): void {
|
||||
this.detachInternal();
|
||||
if (this.activeAnimationPlayers.length == 0) {
|
||||
this.renderer.detachView(this.flatRootNodes);
|
||||
} else {
|
||||
var player = new AnimationGroupPlayer(this.activeAnimationPlayers.getAllPlayers());
|
||||
player.onDone(() => {
|
||||
this.renderer.detachView(this.flatRootNodes);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get changeDetectorRef(): ChangeDetectorRef { return this.ref; }
|
||||
|
||||
get parent(): AppView<any> {
|
||||
@ -319,6 +375,16 @@ export class DebugAppView<T> extends AppView<T> {
|
||||
}
|
||||
}
|
||||
|
||||
detach(): void {
|
||||
this._resetDebug();
|
||||
try {
|
||||
super.detach();
|
||||
} catch (e) {
|
||||
this._rethrowWithContext(e, e.stack);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
destroyLocal() {
|
||||
this._resetDebug();
|
||||
try {
|
||||
|
@ -5,6 +5,7 @@ import 'package:angular2/src/core/change_detection/change_detection.dart';
|
||||
import './metadata/di.dart';
|
||||
import './metadata/directives.dart';
|
||||
import './metadata/view.dart';
|
||||
import './metadata/animations.dart' show AnimationEntryMetadata;
|
||||
|
||||
export './metadata/di.dart';
|
||||
export './metadata/directives.dart';
|
||||
@ -72,7 +73,8 @@ class Component extends ComponentMetadata {
|
||||
dynamic pipes,
|
||||
ViewEncapsulation encapsulation,
|
||||
List<String> styles,
|
||||
List<String> styleUrls})
|
||||
List<String> styleUrls,
|
||||
List<AnimationEntryMetadata> animations})
|
||||
: super(
|
||||
selector: selector,
|
||||
inputs: inputs,
|
||||
@ -92,7 +94,8 @@ class Component extends ComponentMetadata {
|
||||
pipes: pipes,
|
||||
encapsulation: encapsulation,
|
||||
styles: styles,
|
||||
styleUrls: styleUrls);
|
||||
styleUrls: styleUrls,
|
||||
animations: animations);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,7 +109,8 @@ class View extends ViewMetadata {
|
||||
dynamic pipes,
|
||||
ViewEncapsulation encapsulation,
|
||||
List<String> styles,
|
||||
List<String> styleUrls})
|
||||
List<String> styleUrls,
|
||||
List<AnimationEntryMetadata> animations})
|
||||
: super(
|
||||
templateUrl: templateUrl,
|
||||
template: template,
|
||||
@ -114,7 +118,8 @@ class View extends ViewMetadata {
|
||||
pipes: pipes,
|
||||
encapsulation: encapsulation,
|
||||
styles: styles,
|
||||
styleUrls: styleUrls);
|
||||
styleUrls: styleUrls,
|
||||
animations: animations);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,7 +57,8 @@ import {
|
||||
} from './metadata/directives';
|
||||
|
||||
import {ViewMetadata, ViewEncapsulation} from './metadata/view';
|
||||
import {ChangeDetectionStrategy} from './change_detection/change_detection';
|
||||
import {AnimationEntryMetadata} from './animation/metadata';
|
||||
import {ChangeDetectionStrategy} from '../src/change_detection/change_detection';
|
||||
|
||||
import {
|
||||
makeDecorator,
|
||||
@ -91,6 +92,7 @@ export interface ComponentDecorator extends TypeDecorator {
|
||||
renderer?: string,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
animations?: AnimationEntryMetadata[]
|
||||
}): ViewDecorator;
|
||||
}
|
||||
|
||||
@ -111,6 +113,7 @@ export interface ViewDecorator extends TypeDecorator {
|
||||
renderer?: string,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
animations?: AnimationEntryMetadata[]
|
||||
}): ViewDecorator;
|
||||
}
|
||||
|
||||
@ -219,6 +222,7 @@ export interface ComponentMetadataFactory {
|
||||
template?: string,
|
||||
styleUrls?: string[],
|
||||
styles?: string[],
|
||||
animations?: AnimationEntryMetadata[],
|
||||
directives?: Array<Type | any[]>,
|
||||
pipes?: Array<Type | any[]>,
|
||||
encapsulation?: ViewEncapsulation
|
||||
@ -240,6 +244,7 @@ export interface ComponentMetadataFactory {
|
||||
template?: string,
|
||||
styleUrls?: string[],
|
||||
styles?: string[],
|
||||
animations?: AnimationEntryMetadata[],
|
||||
directives?: Array<Type | any[]>,
|
||||
pipes?: Array<Type | any[]>,
|
||||
encapsulation?: ViewEncapsulation
|
||||
@ -297,6 +302,7 @@ export interface ViewMetadataFactory {
|
||||
encapsulation?: ViewEncapsulation,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
animations?: AnimationEntryMetadata[]
|
||||
}): ViewDecorator;
|
||||
new (obj: {
|
||||
templateUrl?: string,
|
||||
@ -306,6 +312,7 @@ export interface ViewMetadataFactory {
|
||||
encapsulation?: ViewEncapsulation,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
animations?: AnimationEntryMetadata[]
|
||||
}): ViewMetadata;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ import {isPresent, Type} from '../../src/facade/lang';
|
||||
import {InjectableMetadata} from '../di/metadata';
|
||||
import {ViewEncapsulation} from './view';
|
||||
import {ChangeDetectionStrategy} from '../change_detection/constants';
|
||||
import {AnimationEntryMetadata} from '../animation/metadata';
|
||||
|
||||
/**
|
||||
* Directives allow you to attach behavior to elements in the DOM.
|
||||
@ -872,6 +873,8 @@ export class ComponentMetadata extends DirectiveMetadata {
|
||||
|
||||
styles: string[];
|
||||
|
||||
animations: AnimationEntryMetadata[];
|
||||
|
||||
directives: Array<Type | any[]>;
|
||||
|
||||
pipes: Array<Type | any[]>;
|
||||
@ -881,7 +884,7 @@ export class ComponentMetadata extends DirectiveMetadata {
|
||||
constructor({selector, inputs, outputs, properties, events, host, exportAs, moduleId,
|
||||
providers, viewProviders,
|
||||
changeDetection = ChangeDetectionStrategy.Default, queries, templateUrl, template,
|
||||
styleUrls, styles, directives, pipes, encapsulation}: {
|
||||
styleUrls, styles, animations, directives, pipes, encapsulation}: {
|
||||
selector?: string,
|
||||
inputs?: string[],
|
||||
outputs?: string[],
|
||||
@ -898,6 +901,7 @@ export class ComponentMetadata extends DirectiveMetadata {
|
||||
template?: string,
|
||||
styleUrls?: string[],
|
||||
styles?: string[],
|
||||
animations?: AnimationEntryMetadata[],
|
||||
directives?: Array<Type | any[]>,
|
||||
pipes?: Array<Type | any[]>,
|
||||
encapsulation?: ViewEncapsulation
|
||||
@ -924,6 +928,7 @@ export class ComponentMetadata extends DirectiveMetadata {
|
||||
this.pipes = pipes;
|
||||
this.encapsulation = encapsulation;
|
||||
this.moduleId = moduleId;
|
||||
this.animations = animations;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {Type} from '../../src/facade/lang';
|
||||
import {AnimationEntryMetadata} from '../animation/metadata';
|
||||
|
||||
/**
|
||||
* Defines template and style encapsulation options available for Component's {@link View}.
|
||||
@ -123,7 +124,10 @@ export class ViewMetadata {
|
||||
*/
|
||||
encapsulation: ViewEncapsulation;
|
||||
|
||||
constructor({templateUrl, template, directives, pipes, encapsulation, styles, styleUrls}: {
|
||||
animations: AnimationEntryMetadata[];
|
||||
|
||||
constructor({templateUrl, template, directives, pipes, encapsulation, styles, styleUrls,
|
||||
animations}: {
|
||||
templateUrl?: string,
|
||||
template?: string,
|
||||
directives?: Array<Type | any[]>,
|
||||
@ -131,6 +135,7 @@ export class ViewMetadata {
|
||||
encapsulation?: ViewEncapsulation,
|
||||
styles?: string[],
|
||||
styleUrls?: string[],
|
||||
animations?: AnimationEntryMetadata[]
|
||||
} = {}) {
|
||||
this.templateUrl = templateUrl;
|
||||
this.template = template;
|
||||
@ -139,5 +144,6 @@ export class ViewMetadata {
|
||||
this.directives = directives;
|
||||
this.pipes = pipes;
|
||||
this.encapsulation = encapsulation;
|
||||
this.animations = animations;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
import {unimplemented} from '../../src/facade/exceptions';
|
||||
import {ViewEncapsulation} from '../metadata/view';
|
||||
import {Injector} from '../di/injector';
|
||||
import {AnimationKeyframe} from '../../src/animation/animation_keyframe';
|
||||
import {AnimationPlayer} from '../../src/animation/animation_player';
|
||||
import {AnimationStyles} from '../../src/animation/animation_styles';
|
||||
|
||||
export class RenderComponentType {
|
||||
constructor(public id: string, public templateUrl: string, public slotCount: number,
|
||||
@ -59,6 +62,8 @@ export abstract class Renderer {
|
||||
abstract invokeElementMethod(renderElement: any, methodName: string, args: any[]);
|
||||
|
||||
abstract setText(renderNode: any, text: string);
|
||||
|
||||
abstract animate(element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[], duration: number, delay: number, easing: string): AnimationPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,85 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit
|
||||
} from '../../testing/testing_internal';
|
||||
|
||||
import {
|
||||
fakeAsync,
|
||||
flushMicrotasks
|
||||
} from '../../testing';
|
||||
|
||||
import {el} from '@angular/platform-browser/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {isPresent} from '../../src/facade/lang';
|
||||
import {MockAnimationPlayer} from '../../testing/animation/mock_animation_player';
|
||||
import {ActiveAnimationPlayersMap} from '../../src/animation/active_animation_players_map';
|
||||
|
||||
export function main() {
|
||||
describe('ActiveAnimationsPlayersMap', function() {
|
||||
var playersMap;
|
||||
var elementNode;
|
||||
var animationName = 'animationName';
|
||||
|
||||
beforeEach(() => {
|
||||
playersMap = new ActiveAnimationPlayersMap();
|
||||
elementNode = el('<div></div>');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
getDOM().remove(elementNode);
|
||||
elementNode = null;
|
||||
});
|
||||
|
||||
it('should register a player an allow it to be accessed', () => {
|
||||
var player = new MockAnimationPlayer();
|
||||
playersMap.set(elementNode, animationName, player);
|
||||
|
||||
expect(playersMap.find(elementNode, animationName)).toBe(player);
|
||||
expect(playersMap.findAllPlayersByElement(elementNode)).toEqual([player]);
|
||||
expect(playersMap.getAllPlayers()).toEqual([player]);
|
||||
expect(playersMap.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should remove a registered player when remove() is called', () => {
|
||||
var player = new MockAnimationPlayer();
|
||||
playersMap.set(elementNode, animationName, player);
|
||||
expect(playersMap.find(elementNode, animationName)).toBe(player);
|
||||
expect(playersMap.length).toEqual(1);
|
||||
playersMap.remove(elementNode, animationName);
|
||||
expect(playersMap.find(elementNode, animationName)).not.toBe(player);
|
||||
expect(playersMap.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should allow multiple players to be registered on the same element', () => {
|
||||
var player1 = new MockAnimationPlayer();
|
||||
var player2 = new MockAnimationPlayer();
|
||||
playersMap.set(elementNode, 'myAnimation1', player1);
|
||||
playersMap.set(elementNode, 'myAnimation2', player2);
|
||||
expect(playersMap.length).toEqual(2);
|
||||
expect(playersMap.findAllPlayersByElement(elementNode)).toEqual([
|
||||
player1,
|
||||
player2
|
||||
]);
|
||||
});
|
||||
|
||||
it('should only allow one player to be set for a given element/animationName pair', () => {
|
||||
var player1 = new MockAnimationPlayer();
|
||||
var player2 = new MockAnimationPlayer();
|
||||
playersMap.set(elementNode, animationName, player1);
|
||||
expect(playersMap.find(elementNode, animationName)).toBe(player1);
|
||||
expect(playersMap.length).toEqual(1);
|
||||
playersMap.set(elementNode, animationName, player2);
|
||||
expect(playersMap.find(elementNode, animationName)).toBe(player2);
|
||||
expect(playersMap.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit
|
||||
} from '../../testing/testing_internal';
|
||||
|
||||
import {
|
||||
fakeAsync,
|
||||
flushMicrotasks
|
||||
} from '../../testing';
|
||||
|
||||
import {isPresent} from '../../src/facade/lang';
|
||||
import {AnimationGroupPlayer} from '../../src/animation/animation_group_player';
|
||||
import {MockAnimationPlayer} from '../../testing/animation/mock_animation_player';
|
||||
|
||||
export function main() {
|
||||
describe('AnimationGroupPlayer', function() {
|
||||
var players;
|
||||
beforeEach(() => {
|
||||
players = [
|
||||
new MockAnimationPlayer(),
|
||||
new MockAnimationPlayer(),
|
||||
new MockAnimationPlayer(),
|
||||
];
|
||||
});
|
||||
|
||||
var assertLastStatus =
|
||||
(player: MockAnimationPlayer, status: string, match: boolean, iOffset: number = 0) => {
|
||||
var index = player.log.length - 1 + iOffset;
|
||||
var actual = player.log.length > 0 ? player.log[index] : null;
|
||||
if (match) {
|
||||
expect(actual).toEqual(status);
|
||||
} else {
|
||||
expect(actual).not.toEqual(status);
|
||||
}
|
||||
}
|
||||
|
||||
var assertPlaying = (player: MockAnimationPlayer, isPlaying: boolean) => {
|
||||
assertLastStatus(player, 'play', isPlaying);
|
||||
};
|
||||
|
||||
it('should play and pause all players in parallel', () => {
|
||||
var group = new AnimationGroupPlayer(players);
|
||||
|
||||
assertPlaying(players[0], false);
|
||||
assertPlaying(players[1], false);
|
||||
assertPlaying(players[2], false);
|
||||
|
||||
group.play();
|
||||
|
||||
assertPlaying(players[0], true);
|
||||
assertPlaying(players[1], true);
|
||||
assertPlaying(players[2], true);
|
||||
|
||||
group.pause();
|
||||
|
||||
assertPlaying(players[0], false);
|
||||
assertPlaying(players[1], false);
|
||||
assertPlaying(players[2], false);
|
||||
});
|
||||
|
||||
it('should finish when all players have finished', () => {
|
||||
var group = new AnimationGroupPlayer(players);
|
||||
var completed = false;
|
||||
group.onDone(() => completed = true);
|
||||
|
||||
group.play();
|
||||
|
||||
expect(completed).toBeFalsy();
|
||||
|
||||
players[0].finish();
|
||||
|
||||
expect(completed).toBeFalsy();
|
||||
|
||||
players[1].finish();
|
||||
|
||||
expect(completed).toBeFalsy();
|
||||
|
||||
players[2].finish();
|
||||
|
||||
expect(completed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should restart all the players', () => {
|
||||
var group = new AnimationGroupPlayer(players);
|
||||
|
||||
group.play();
|
||||
|
||||
assertLastStatus(players[0], 'restart', false);
|
||||
assertLastStatus(players[1], 'restart', false);
|
||||
assertLastStatus(players[2], 'restart', false);
|
||||
|
||||
group.restart();
|
||||
|
||||
assertLastStatus(players[0], 'restart', true);
|
||||
assertLastStatus(players[1], 'restart', true);
|
||||
assertLastStatus(players[2], 'restart', true);
|
||||
});
|
||||
|
||||
it('should finish all the players', () => {
|
||||
var group = new AnimationGroupPlayer(players);
|
||||
|
||||
var completed = false;
|
||||
group.onDone(() => completed = true);
|
||||
|
||||
expect(completed).toBeFalsy();
|
||||
|
||||
group.play();
|
||||
|
||||
assertLastStatus(players[0], 'finish', false);
|
||||
assertLastStatus(players[1], 'finish', false);
|
||||
assertLastStatus(players[2], 'finish', false);
|
||||
|
||||
expect(completed).toBeFalsy();
|
||||
|
||||
group.finish();
|
||||
|
||||
assertLastStatus(players[0], 'finish', true, -1);
|
||||
assertLastStatus(players[1], 'finish', true, -1);
|
||||
assertLastStatus(players[2], 'finish', true, -1);
|
||||
|
||||
assertLastStatus(players[0], 'destroy', true);
|
||||
assertLastStatus(players[1], 'destroy', true);
|
||||
assertLastStatus(players[2], 'destroy', true);
|
||||
|
||||
expect(completed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should call destroy automatically when finished if no parent player is present', () => {
|
||||
var group = new AnimationGroupPlayer(players);
|
||||
|
||||
group.play();
|
||||
|
||||
assertLastStatus(players[0], 'destroy', false);
|
||||
assertLastStatus(players[1], 'destroy', false);
|
||||
assertLastStatus(players[2], 'destroy', false);
|
||||
|
||||
group.finish();
|
||||
|
||||
assertLastStatus(players[0], 'destroy', true);
|
||||
assertLastStatus(players[1], 'destroy', true);
|
||||
assertLastStatus(players[2], 'destroy', true);
|
||||
});
|
||||
|
||||
it('should not call destroy automatically when finished if a parent player is present', () => {
|
||||
var group = new AnimationGroupPlayer(players);
|
||||
var parent = new AnimationGroupPlayer([group, new MockAnimationPlayer()]);
|
||||
|
||||
group.play();
|
||||
|
||||
assertLastStatus(players[0], 'destroy', false);
|
||||
assertLastStatus(players[1], 'destroy', false);
|
||||
assertLastStatus(players[2], 'destroy', false);
|
||||
|
||||
group.finish();
|
||||
|
||||
assertLastStatus(players[0], 'destroy', false);
|
||||
assertLastStatus(players[1], 'destroy', false);
|
||||
assertLastStatus(players[2], 'destroy', false);
|
||||
|
||||
parent.finish();
|
||||
|
||||
assertLastStatus(players[0], 'destroy', true);
|
||||
assertLastStatus(players[1], 'destroy', true);
|
||||
assertLastStatus(players[2], 'destroy', true);
|
||||
});
|
||||
|
||||
it('should function without any players', () => {
|
||||
var group = new AnimationGroupPlayer([]);
|
||||
group.onDone(() => {});
|
||||
group.pause();
|
||||
group.play();
|
||||
group.finish();
|
||||
group.restart();
|
||||
group.destroy();
|
||||
});
|
||||
|
||||
it('should call onDone after the next microtask if no players are provided', fakeAsync(() => {
|
||||
var group = new AnimationGroupPlayer([]);
|
||||
var completed = false;
|
||||
group.onDone(() => completed = true);
|
||||
expect(completed).toEqual(false);
|
||||
flushMicrotasks();
|
||||
expect(completed).toEqual(true);
|
||||
}));
|
||||
});
|
||||
}
|
@ -0,0 +1,899 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
beforeEachProviders
|
||||
} from '../../testing/testing_internal';
|
||||
|
||||
import {TestComponentBuilder} from '@angular/compiler/testing';
|
||||
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {
|
||||
fakeAsync,
|
||||
flushMicrotasks,
|
||||
tick
|
||||
} from '../../testing';
|
||||
|
||||
import {isPresent, isArray, IS_DART} from '../../src/facade/lang';
|
||||
|
||||
import {provide, Component} from '../../index';
|
||||
|
||||
import {NgIf, NgFor, AsyncPipe} from '@angular/common';
|
||||
|
||||
import {CompilerConfig} from '@angular/compiler';
|
||||
import {AnimationDriver} from '../../src/animation/animation_driver';
|
||||
import {MockAnimationDriver} from '../../testing/animation/mock_animation_driver';
|
||||
import {trigger, state, transition, keyframes, style, animate, group, sequence, AnimationEntryMetadata} from '../../src/animation/metadata';
|
||||
|
||||
import {AnimationStyleUtil} from '../../src/animation/animation_style_util';
|
||||
|
||||
import {AUTO_STYLE} from '../../src/animation/metadata';
|
||||
|
||||
export function main() {
|
||||
if (IS_DART) {
|
||||
declareTests();
|
||||
} else {
|
||||
describe('jit', () => {
|
||||
beforeEachProviders(
|
||||
() => [provide(CompilerConfig, {useValue: new CompilerConfig(true, false, true)})]);
|
||||
declareTests();
|
||||
});
|
||||
|
||||
describe('no jit', () => {
|
||||
beforeEachProviders(
|
||||
() => [provide(CompilerConfig, {useValue: new CompilerConfig(true, false, false)})]);
|
||||
declareTests();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function declareTests() {
|
||||
describe('animation tests', function() {
|
||||
beforeEachProviders(() => [provide(AnimationDriver, {useClass: MockAnimationDriver})]);
|
||||
|
||||
var makeAnimationCmp = (tcb: TestComponentBuilder, tpl: string, animationEntry: AnimationEntryMetadata|AnimationEntryMetadata[], callback = null) => {
|
||||
var entries = isArray(animationEntry)
|
||||
? <AnimationEntryMetadata[]>animationEntry
|
||||
: [<AnimationEntryMetadata>animationEntry];
|
||||
tcb = tcb.overrideTemplate(DummyIfCmp, tpl);
|
||||
tcb = tcb.overrideAnimations(DummyIfCmp, entries);
|
||||
tcb.createAsync(DummyIfCmp).then((root) => { callback(root); });
|
||||
tick();
|
||||
};
|
||||
|
||||
describe('animation triggers', () => {
|
||||
it('should trigger a state change animation from void => state',
|
||||
inject([TestComponentBuilder, AnimationDriver],
|
||||
fakeAsync(
|
||||
(tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
makeAnimationCmp(tcb, '<div *ngIf="exp" @myAnimation="exp"></div>',
|
||||
trigger('myAnimation', [
|
||||
transition('void => *', [
|
||||
style({'opacity': 0}),
|
||||
animate(500,
|
||||
style({'opacity': 1}))
|
||||
])
|
||||
]),
|
||||
(fixture) => {
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.length).toEqual(1);
|
||||
|
||||
var keyframes2 = driver.log[0]['keyframeLookup'];
|
||||
expect(keyframes2.length).toEqual(2);
|
||||
expect(keyframes2[0]).toEqual([0, {'opacity': 0}]);
|
||||
expect(keyframes2[1]).toEqual([1, {'opacity': 1}]);
|
||||
});
|
||||
})));
|
||||
|
||||
it('should trigger a state change animation from state => void',
|
||||
inject([TestComponentBuilder, AnimationDriver],
|
||||
fakeAsync(
|
||||
(tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
makeAnimationCmp(tcb, '<div *ngIf="exp" @myAnimation="exp"></div>',
|
||||
trigger('myAnimation', [
|
||||
transition('* => void', [
|
||||
style({'opacity': 1}),
|
||||
animate(500,
|
||||
style({'opacity': 0}))
|
||||
])
|
||||
]),
|
||||
(fixture) => {
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
cmp.exp = false;
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.length).toEqual(1);
|
||||
|
||||
var keyframes2 = driver.log[0]['keyframeLookup'];
|
||||
expect(keyframes2.length).toEqual(2);
|
||||
expect(keyframes2[0]).toEqual([0, {'opacity': 1}]);
|
||||
expect(keyframes2[1]).toEqual([1, {'opacity': 0}]);
|
||||
});
|
||||
})));
|
||||
|
||||
it('should animate the element when the expression changes between states',
|
||||
inject([TestComponentBuilder, AnimationDriver],
|
||||
fakeAsync(
|
||||
(tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
tcb.overrideAnimations(DummyIfCmp, [
|
||||
trigger("myAnimation", [
|
||||
transition("* => state1", [
|
||||
style({'background': 'red'}),
|
||||
animate('0.5s 1s ease-out',
|
||||
style({'background': 'blue'}))
|
||||
])
|
||||
])
|
||||
]).createAsync(DummyIfCmp)
|
||||
.then((fixture) => {
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
cmp.exp = 'state1';
|
||||
fixture.detectChanges();
|
||||
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.length).toEqual(1);
|
||||
|
||||
var animation1 = driver.log[0];
|
||||
expect(animation1['duration']).toEqual(500);
|
||||
expect(animation1['delay']).toEqual(1000);
|
||||
expect(animation1['easing']).toEqual('ease-out');
|
||||
|
||||
var startingStyles = animation1['startingStyles'];
|
||||
expect(startingStyles).toEqual({'background': 'red'});
|
||||
|
||||
var keyframes = animation1['keyframeLookup'];
|
||||
expect(keyframes[0]).toEqual([0, {'background': 'red'}]);
|
||||
expect(keyframes[1]).toEqual([1, {'background': 'blue'}]);
|
||||
});
|
||||
})));
|
||||
|
||||
it('should combine repeated style steps into a single step',
|
||||
inject(
|
||||
[TestComponentBuilder, AnimationDriver],
|
||||
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
tcb.overrideAnimations(DummyIfCmp, [
|
||||
trigger("myAnimation", [
|
||||
transition("void => *", [
|
||||
style({'background': 'red'}),
|
||||
style({'width': '100px'}),
|
||||
style({'background': 'gold'}),
|
||||
style({'height': 111}),
|
||||
animate('999ms', style({'width': '200px', 'background': 'blue'})),
|
||||
style({'opacity': '1'}),
|
||||
style({'border-width': '100px'}),
|
||||
animate('999ms', style({'opacity': '0', 'height': '200px', 'border-width': '10px'}))
|
||||
])
|
||||
])
|
||||
])
|
||||
.createAsync(DummyIfCmp)
|
||||
.then((fixture) => {
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.length).toEqual(2);
|
||||
|
||||
var animation1 = driver.log[0];
|
||||
expect(animation1['duration']).toEqual(999);
|
||||
expect(animation1['delay']).toEqual(0);
|
||||
expect(animation1['easing']).toEqual(null);
|
||||
expect(animation1['startingStyles'])
|
||||
.toEqual({'background': 'gold', 'width': '100px', 'height': 111});
|
||||
|
||||
var keyframes1 = animation1['keyframeLookup'];
|
||||
expect(keyframes1[0]).toEqual([0, {'background': 'gold', 'width': '100px'}]);
|
||||
expect(keyframes1[1]).toEqual([1, {'background': 'blue', 'width': '200px'}]);
|
||||
|
||||
var animation2 = driver.log[1];
|
||||
expect(animation2['duration']).toEqual(999);
|
||||
expect(animation2['delay']).toEqual(0);
|
||||
expect(animation2['easing']).toEqual(null);
|
||||
expect(animation2['startingStyles'])
|
||||
.toEqual({'opacity': '1', 'border-width': '100px'});
|
||||
|
||||
var keyframes2 = animation2['keyframeLookup'];
|
||||
expect(keyframes2[0])
|
||||
.toEqual([0, {'opacity': '1', 'height': 111, 'border-width': '100px'}]);
|
||||
expect(keyframes2[1])
|
||||
.toEqual([1, {'opacity': '0', 'height': '200px', 'border-width': '10px'}]);
|
||||
});
|
||||
})));
|
||||
|
||||
describe('groups/sequences', () => {
|
||||
var assertPlaying =
|
||||
(player: MockAnimationDriver, isPlaying) => {
|
||||
var method = 'play';
|
||||
var lastEntry = player.log.length > 0 ? player.log[player.log.length - 1] : null;
|
||||
if (isPresent(lastEntry)) {
|
||||
if (isPlaying) {
|
||||
expect(lastEntry).toEqual(method);
|
||||
} else {
|
||||
expect(lastEntry).not.toEqual(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it('should run animations in sequence one by one if a top-level array is used',
|
||||
inject([TestComponentBuilder, AnimationDriver],
|
||||
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
tcb.overrideAnimations(DummyIfCmp, [
|
||||
trigger("myAnimation", [
|
||||
transition("void => *", [
|
||||
style({"opacity": '0'}),
|
||||
animate(1000, style({'opacity': '0.5'})),
|
||||
animate('1000ms', style({'opacity': '0.8'})),
|
||||
animate('1s', style({'opacity': '1'})),
|
||||
])
|
||||
])
|
||||
])
|
||||
.createAsync(DummyIfCmp)
|
||||
.then((fixture) => {
|
||||
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.length).toEqual(3);
|
||||
|
||||
var player1 = driver.log[0]['player'];
|
||||
var player2 = driver.log[1]['player'];
|
||||
var player3 = driver.log[2]['player'];
|
||||
|
||||
assertPlaying(player1, true);
|
||||
assertPlaying(player2, false);
|
||||
assertPlaying(player3, false);
|
||||
|
||||
player1.finish();
|
||||
|
||||
assertPlaying(player1, false);
|
||||
assertPlaying(player2, true);
|
||||
assertPlaying(player3, false);
|
||||
|
||||
player2.finish();
|
||||
|
||||
assertPlaying(player1, false);
|
||||
assertPlaying(player2, false);
|
||||
assertPlaying(player3, true);
|
||||
|
||||
player3.finish();
|
||||
|
||||
assertPlaying(player1, false);
|
||||
assertPlaying(player2, false);
|
||||
assertPlaying(player3, false);
|
||||
});
|
||||
})));
|
||||
|
||||
it('should run animations in parallel if a group is used',
|
||||
inject(
|
||||
[TestComponentBuilder, AnimationDriver],
|
||||
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
tcb.overrideAnimations(DummyIfCmp, [
|
||||
trigger("myAnimation", [
|
||||
transition("void => *", [
|
||||
style({'width': 0, 'height': 0}),
|
||||
group([animate(1000, style({'width': 100})), animate(5000, style({'height': 500}))]),
|
||||
group([animate(1000, style({'width': 0})), animate(5000, style({'height': 0}))])
|
||||
])
|
||||
])
|
||||
])
|
||||
.createAsync(DummyIfCmp)
|
||||
.then((fixture) => {
|
||||
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.length).toEqual(5);
|
||||
|
||||
var player1 = driver.log[0]['player'];
|
||||
var player2 = driver.log[1]['player'];
|
||||
var player3 = driver.log[2]['player'];
|
||||
var player4 = driver.log[3]['player'];
|
||||
var player5 = driver.log[4]['player'];
|
||||
|
||||
assertPlaying(player1, true);
|
||||
assertPlaying(player2, false);
|
||||
assertPlaying(player3, false);
|
||||
assertPlaying(player4, false);
|
||||
assertPlaying(player5, false);
|
||||
|
||||
player1.finish();
|
||||
|
||||
assertPlaying(player1, false);
|
||||
assertPlaying(player2, true);
|
||||
assertPlaying(player3, true);
|
||||
assertPlaying(player4, false);
|
||||
assertPlaying(player5, false);
|
||||
|
||||
player2.finish();
|
||||
|
||||
assertPlaying(player1, false);
|
||||
assertPlaying(player2, false);
|
||||
assertPlaying(player3, true);
|
||||
assertPlaying(player4, false);
|
||||
assertPlaying(player5, false);
|
||||
|
||||
player3.finish();
|
||||
|
||||
assertPlaying(player1, false);
|
||||
assertPlaying(player2, false);
|
||||
assertPlaying(player3, false);
|
||||
assertPlaying(player4, true);
|
||||
assertPlaying(player5, true);
|
||||
});
|
||||
})));
|
||||
});
|
||||
|
||||
describe('keyframes', () => {
|
||||
it('should create an animation step with multiple keyframes',
|
||||
inject(
|
||||
[TestComponentBuilder, AnimationDriver],
|
||||
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
tcb.overrideAnimations(DummyIfCmp, [
|
||||
trigger("myAnimation", [
|
||||
transition("void => *", [
|
||||
animate(1000, keyframes([
|
||||
style([{ "width": 0, offset: 0 }]),
|
||||
style([{ "width": 100, offset: 0.25 }]),
|
||||
style([{ "width": 200, offset: 0.75 }]),
|
||||
style([{ "width": 300, offset: 1 }])
|
||||
]))
|
||||
])
|
||||
])
|
||||
])
|
||||
.createAsync(DummyIfCmp)
|
||||
.then((fixture) => {
|
||||
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
var keyframes = driver.log[0]['keyframeLookup'];
|
||||
expect(keyframes.length).toEqual(4);
|
||||
expect(keyframes[0]).toEqual([0, {'width': 0}]);
|
||||
expect(keyframes[1]).toEqual([0.25, {'width': 100}]);
|
||||
expect(keyframes[2]).toEqual([0.75, {'width': 200}]);
|
||||
expect(keyframes[3]).toEqual([1, {'width': 300}]);
|
||||
});
|
||||
})));
|
||||
|
||||
it('should fetch any keyframe styles that are not defined in the first keyframe from the previous entries or getCompuedStyle',
|
||||
inject(
|
||||
[TestComponentBuilder, AnimationDriver],
|
||||
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
tcb.overrideAnimations(DummyIfCmp, [
|
||||
trigger("myAnimation", [
|
||||
transition("void => *", [
|
||||
style({ "color": "white" }),
|
||||
animate(1000, style({ "color": "silver" })),
|
||||
animate(1000, keyframes([
|
||||
style([{ "color": "gold", offset: 0.25 }]),
|
||||
style([{ "color": "bronze", "background-color":"teal", offset: 0.50 }]),
|
||||
style([{ "color": "platinum", offset: 0.75 }]),
|
||||
style([{ "color": "diamond", offset: 1 }])
|
||||
]))
|
||||
])
|
||||
])
|
||||
])
|
||||
.createAsync(DummyIfCmp)
|
||||
.then((fixture) => {
|
||||
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
var keyframes = driver.log[1]['keyframeLookup'];
|
||||
expect(keyframes.length).toEqual(5);
|
||||
expect(keyframes[0]).toEqual([0, {"color": "silver", "background-color":AUTO_STYLE }]);
|
||||
expect(keyframes[1]).toEqual([0.25, {"color": "gold"}]);
|
||||
expect(keyframes[2]).toEqual([0.50, {"color": "bronze", "background-color":"teal"}]);
|
||||
expect(keyframes[3]).toEqual([0.75, {"color": "platinum"}]);
|
||||
expect(keyframes[4]).toEqual([1, {"color": "diamond", "background-color":"teal"}]);
|
||||
});
|
||||
})));
|
||||
});
|
||||
|
||||
it('should cancel the previously running animation active with the same element/animationName pair',
|
||||
inject([TestComponentBuilder, AnimationDriver],
|
||||
fakeAsync(
|
||||
(tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
tcb.overrideAnimations(DummyIfCmp, [
|
||||
trigger("myAnimation", [
|
||||
transition("* => *", [
|
||||
style({ "opacity":0 }),
|
||||
animate(500, style({ "opacity":1 }))
|
||||
])
|
||||
])
|
||||
])
|
||||
.createAsync(DummyIfCmp)
|
||||
.then((fixture) => {
|
||||
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
|
||||
cmp.exp = "state1";
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
var enterCompleted = false;
|
||||
var enterPlayer = driver.log[0]['player'];
|
||||
enterPlayer.onDone(() => enterCompleted = true);
|
||||
|
||||
expect(enterCompleted).toEqual(false);
|
||||
|
||||
cmp.exp = "state2";
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(enterCompleted).toEqual(true);
|
||||
});
|
||||
})));
|
||||
|
||||
it('should destroy all animation players once the animation is complete',
|
||||
inject([TestComponentBuilder, AnimationDriver],
|
||||
fakeAsync(
|
||||
(tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
tcb.overrideAnimations(DummyIfCmp, [
|
||||
trigger("myAnimation", [
|
||||
transition("void => *", [
|
||||
style({'background': 'red', 'opacity': 0.5}),
|
||||
animate(500, style({'background': 'black'})),
|
||||
group([
|
||||
animate(500, style({'background': 'black'})),
|
||||
animate(1000, style({'opacity': '0.2'})),
|
||||
]),
|
||||
sequence([
|
||||
animate(500, style({'opacity': '1'})),
|
||||
animate(1000, style({'background': 'white'}))
|
||||
])
|
||||
])
|
||||
])
|
||||
]).createAsync(DummyIfCmp)
|
||||
.then((fixture) => {
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.length).toEqual(5);
|
||||
|
||||
driver.log.forEach(entry => entry['player'].finish());
|
||||
driver.log.forEach(entry => {
|
||||
var player = <MockAnimationDriver>entry['player'];
|
||||
expect(player.log[player.log.length - 2]).toEqual('finish');
|
||||
expect(player.log[player.log.length - 1]).toEqual('destroy');
|
||||
});
|
||||
});
|
||||
})));
|
||||
|
||||
it('should use first matched animation when multiple animations are registered',
|
||||
inject(
|
||||
[TestComponentBuilder, AnimationDriver],
|
||||
fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
tcb = tcb.overrideTemplate(DummyIfCmp, `
|
||||
<div @rotate="exp"></div>
|
||||
<div @rotate="exp2"></div>
|
||||
`);
|
||||
tcb.overrideAnimations(DummyIfCmp, [
|
||||
trigger("rotate", [
|
||||
transition("start => *", [
|
||||
style({'color': 'white'}),
|
||||
animate(500,
|
||||
style({'color': 'red'}))
|
||||
]),
|
||||
transition("start => end", [
|
||||
style({'color': 'white'}),
|
||||
animate(500,
|
||||
style({'color': 'pink'}))
|
||||
])
|
||||
]),
|
||||
]).createAsync(DummyIfCmp)
|
||||
.then((fixture) => {
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
cmp.exp = 'start';
|
||||
cmp.exp2 = 'start';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.length).toEqual(0);
|
||||
|
||||
cmp.exp = 'something';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.length).toEqual(1);
|
||||
|
||||
var animation1 = driver.log[0];
|
||||
var keyframes1 = animation1['keyframeLookup'];
|
||||
var toStyles1 = keyframes1[1][1];
|
||||
expect(toStyles1['color']).toEqual('red');
|
||||
|
||||
cmp.exp2 = 'end';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.length).toEqual(2);
|
||||
|
||||
var animation2 = driver.log[1];
|
||||
var keyframes2 = animation2['keyframeLookup'];
|
||||
var toStyles2 = keyframes2[1][1];
|
||||
expect(toStyles2['color']).toEqual('red');
|
||||
});
|
||||
})));
|
||||
|
||||
it('should not remove the element until the void transition animation is complete',
|
||||
inject([TestComponentBuilder, AnimationDriver], fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
makeAnimationCmp(tcb, '<div class="my-if" *ngIf="exp" @myAnimation></div>',
|
||||
trigger('myAnimation', [
|
||||
transition('* => void', [
|
||||
animate(1000, style({'opacity': 0}))
|
||||
])
|
||||
]), (fixture) => {
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
cmp.exp = false;
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
var player = driver.log[0]['player'];
|
||||
var container = fixture.debugElement.nativeElement;
|
||||
var ifElm = getDOM().querySelector(container, '.my-if');
|
||||
expect(ifElm).toBeTruthy();
|
||||
|
||||
player.finish();
|
||||
ifElm = getDOM().querySelector(container,'.my-if');
|
||||
expect(ifElm).toBeFalsy();
|
||||
});
|
||||
})));
|
||||
|
||||
it('should fill an animation with the missing style values if not defined within an earlier style step',
|
||||
inject([TestComponentBuilder, AnimationDriver], fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
makeAnimationCmp(tcb, '<div @myAnimation="exp"></div>',
|
||||
trigger('myAnimation', [
|
||||
transition('* => *', [
|
||||
animate(1000, style({'opacity': 0})),
|
||||
animate(1000, style({'opacity': 1}))
|
||||
])
|
||||
]), (fixture) => {
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
cmp.exp = 'state1';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
var animation1 = driver.log[0];
|
||||
var keyframes1 = animation1['keyframeLookup'];
|
||||
expect(keyframes1[0]).toEqual([0, {'opacity': AUTO_STYLE}]);
|
||||
expect(keyframes1[1]).toEqual([1, {'opacity': 0}]);
|
||||
|
||||
var animation2 = driver.log[1];
|
||||
var keyframes2 = animation2['keyframeLookup'];
|
||||
expect(keyframes2[0]).toEqual([0, {'opacity': 0}]);
|
||||
expect(keyframes2[1]).toEqual([1, {'opacity': 1}]);
|
||||
});
|
||||
})));
|
||||
|
||||
it('should perform two transitions in parallel if defined in different state triggers',
|
||||
inject([TestComponentBuilder, AnimationDriver], fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
makeAnimationCmp(tcb, '<div @one="exp" @two="exp2"></div>', [
|
||||
trigger('one', [
|
||||
transition('state1 => state2', [
|
||||
style({'opacity': 0}),
|
||||
animate(1000, style({'opacity': 1}))
|
||||
])
|
||||
]),
|
||||
trigger('two', [
|
||||
transition('state1 => state2', [
|
||||
style({'width': 100}),
|
||||
animate(1000, style({'width': 1000}))
|
||||
])
|
||||
])
|
||||
], (fixture) => {
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
cmp.exp = 'state1';
|
||||
cmp.exp2 = 'state1';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
cmp.exp = 'state2';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.length).toEqual(1);
|
||||
|
||||
var count = 0;
|
||||
var animation1 = driver.log[0];
|
||||
var player1 = animation1['player'];
|
||||
player1.onDone(() => count++);
|
||||
|
||||
expect(count).toEqual(0);
|
||||
|
||||
cmp.exp2 = 'state2';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.length).toEqual(2);
|
||||
expect(count).toEqual(0);
|
||||
|
||||
var animation2 = driver.log[1];
|
||||
var player2 = animation2['player'];
|
||||
player2.onDone(() => count++);
|
||||
|
||||
expect(count).toEqual(0);
|
||||
player1.finish();
|
||||
expect(count).toEqual(1);
|
||||
player2.finish();
|
||||
expect(count).toEqual(2);
|
||||
});
|
||||
})));
|
||||
});
|
||||
|
||||
describe('animation states', () => {
|
||||
it('should retain the destination animation state styles once the animation is complete',
|
||||
inject([TestComponentBuilder, AnimationDriver], fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
makeAnimationCmp(tcb, '<div class="target" @status="exp"></div>', [
|
||||
trigger('status', [
|
||||
state('final', style({ "top": '100px' })),
|
||||
transition('* => final', [ animate(1000) ])
|
||||
])
|
||||
], (fixture) => {
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
var node = getDOM().querySelector(fixture.debugElement.nativeElement, '.target');
|
||||
cmp.exp = 'final';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
var animation = driver.log[0];
|
||||
var player = animation['player'];
|
||||
player.finish();
|
||||
|
||||
expect(getDOM().getStyle(node, 'top')).toEqual('100px');
|
||||
});
|
||||
})));
|
||||
|
||||
it('should seed in the origin animation state styles into the first animation step',
|
||||
inject([TestComponentBuilder, AnimationDriver], fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
makeAnimationCmp(tcb, '<div class="target" @status="exp"></div>', [
|
||||
trigger('status', [
|
||||
state('void', style({ "height": '100px' })),
|
||||
transition('* => *', [ animate(1000) ])
|
||||
])
|
||||
], (fixture) => {
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
var node = getDOM().querySelector(fixture.debugElement.nativeElement, '.target');
|
||||
cmp.exp = 'final';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
var animation = driver.log[0];
|
||||
expect(animation['startingStyles']).toEqual({
|
||||
"height": "100px"
|
||||
});
|
||||
});
|
||||
})));
|
||||
|
||||
it('should perform a state change even if there is no transition that is found',
|
||||
inject([TestComponentBuilder, AnimationDriver], fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
makeAnimationCmp(tcb, '<div class="target" @status="exp"></div>', [
|
||||
trigger('status', [
|
||||
state('void', style({ "width": '0px' })),
|
||||
state('final', style({ "width": '100px' })),
|
||||
])
|
||||
], (fixture) => {
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
var node = getDOM().querySelector(fixture.debugElement.nativeElement, '.target');
|
||||
cmp.exp = 'final';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.length).toEqual(0);
|
||||
flushMicrotasks();
|
||||
|
||||
expect(getDOM().getStyle(node, 'width')).toEqual('100px');
|
||||
});
|
||||
})));
|
||||
|
||||
it('should allow multiple states to be defined with the same styles',
|
||||
inject([TestComponentBuilder, AnimationDriver], fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
makeAnimationCmp(tcb, '<div class="target" @status="exp"></div>', [
|
||||
trigger('status', [
|
||||
state('a, c', style({ "height": '100px' })),
|
||||
state('b, d', style({ "width": '100px' })),
|
||||
])
|
||||
], (fixture) => {
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
var node = getDOM().querySelector(fixture.debugElement.nativeElement, '.target');
|
||||
|
||||
cmp.exp = 'a';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(getDOM().getStyle(node, 'height')).toEqual('100px');
|
||||
expect(getDOM().getStyle(node, 'width')).not.toEqual('100px');
|
||||
|
||||
cmp.exp = 'b';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(getDOM().getStyle(node, 'height')).not.toEqual('100px');
|
||||
expect(getDOM().getStyle(node, 'width')).toEqual('100px');
|
||||
|
||||
cmp.exp = 'c';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(getDOM().getStyle(node, 'height')).toEqual('100px');
|
||||
expect(getDOM().getStyle(node, 'width')).not.toEqual('100px');
|
||||
|
||||
cmp.exp = 'd';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(getDOM().getStyle(node, 'height')).not.toEqual('100px');
|
||||
expect(getDOM().getStyle(node, 'width')).toEqual('100px');
|
||||
|
||||
cmp.exp = 'e';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(getDOM().getStyle(node, 'height')).not.toEqual('100px');
|
||||
expect(getDOM().getStyle(node, 'width')).not.toEqual('100px');
|
||||
});
|
||||
})));
|
||||
|
||||
it('should allow multiple transitions to be defined with the same sequence',
|
||||
inject([TestComponentBuilder, AnimationDriver], fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
makeAnimationCmp(tcb, '<div class="target" @status="exp"></div>', [
|
||||
trigger('status', [
|
||||
transition('a => b, b => c', [
|
||||
animate(1000)
|
||||
]),
|
||||
transition('* => *', [
|
||||
animate(300)
|
||||
])
|
||||
])
|
||||
], (fixture) => {
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
var node = getDOM().querySelector(fixture.debugElement.nativeElement, '.target');
|
||||
|
||||
cmp.exp = 'a';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.pop()['duration']).toEqual(300);
|
||||
|
||||
cmp.exp = 'b';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.pop()['duration']).toEqual(1000);
|
||||
|
||||
cmp.exp = 'c';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.pop()['duration']).toEqual(1000);
|
||||
|
||||
cmp.exp = 'd';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(driver.log.pop()['duration']).toEqual(300);
|
||||
});
|
||||
})));
|
||||
|
||||
it('should balance the animation with the origin/destination styles as keyframe animation properties',
|
||||
inject([TestComponentBuilder, AnimationDriver], fakeAsync((tcb: TestComponentBuilder, driver: MockAnimationDriver) => {
|
||||
makeAnimationCmp(tcb, '<div class="target" @status="exp"></div>', [
|
||||
trigger('status', [
|
||||
state('void', style({ "height": "100px", "opacity":0 })),
|
||||
state('final', style({ "height": "333px", "width":"200px" })),
|
||||
transition('void => final', [
|
||||
animate(1000)
|
||||
])
|
||||
])
|
||||
], (fixture) => {
|
||||
tick();
|
||||
|
||||
var cmp = fixture.debugElement.componentInstance;
|
||||
var node = getDOM().querySelector(fixture.debugElement.nativeElement, '.target');
|
||||
|
||||
cmp.exp = 'final';
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
var animation = driver.log.pop();
|
||||
var keyframes = animation['keyframeLookup'];
|
||||
|
||||
expect(keyframes[0]).toEqual(
|
||||
[0, { "height": "100px",
|
||||
"opacity": 0,
|
||||
"width": AUTO_STYLE }]);
|
||||
|
||||
expect(keyframes[1]).toEqual(
|
||||
[1, { "height": "333px",
|
||||
"opacity": AUTO_STYLE,
|
||||
"width": "200px" }]);
|
||||
});
|
||||
})));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'if-cmp',
|
||||
directives: [NgIf],
|
||||
template: `
|
||||
<div *ngIf="exp" @myAnimation="exp"></div>
|
||||
`
|
||||
})
|
||||
class DummyIfCmp {
|
||||
exp = false;
|
||||
exp2 = false;
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit
|
||||
} from '../../testing/testing_internal';
|
||||
|
||||
import {
|
||||
fakeAsync,
|
||||
flushMicrotasks
|
||||
} from '../../testing';
|
||||
|
||||
import {NoOpAnimationPlayer, AnimationPlayer} from '../../src/animation/animation_player';
|
||||
|
||||
export function main() {
|
||||
describe('NoOpAnimationPlayer', function() {
|
||||
it('should call onDone after the next microtask when constructed', fakeAsync(() => {
|
||||
var player = new NoOpAnimationPlayer();
|
||||
var completed = false;
|
||||
player.onDone(() => completed = true);
|
||||
expect(completed).toEqual(false);
|
||||
flushMicrotasks();
|
||||
expect(completed).toEqual(true);
|
||||
}));
|
||||
|
||||
it('should be able to run each of the player methods', fakeAsync(() => {
|
||||
var player = new NoOpAnimationPlayer();
|
||||
player.pause();
|
||||
player.play();
|
||||
player.finish();
|
||||
player.restart();
|
||||
player.destroy();
|
||||
}));
|
||||
});
|
||||
}
|
@ -0,0 +1,216 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit
|
||||
} from '../../testing/testing_internal';
|
||||
|
||||
import {
|
||||
fakeAsync,
|
||||
flushMicrotasks
|
||||
} from '../../testing';
|
||||
|
||||
import {isPresent} from '../../src/facade/lang';
|
||||
import {AnimationSequencePlayer} from '../../src/animation/animation_sequence_player';
|
||||
import {MockAnimationPlayer} from '../../testing/animation/mock_animation_player';
|
||||
|
||||
export function main() {
|
||||
describe('AnimationSequencePlayer', function() {
|
||||
var players;
|
||||
beforeEach(() => {
|
||||
players = [
|
||||
new MockAnimationPlayer(),
|
||||
new MockAnimationPlayer(),
|
||||
new MockAnimationPlayer(),
|
||||
];
|
||||
});
|
||||
|
||||
var assertLastStatus =
|
||||
(player: MockAnimationPlayer, status: string, match: boolean, iOffset: number = 0) => {
|
||||
var index = player.log.length - 1 + iOffset;
|
||||
var actual = player.log.length > 0 ? player.log[index] : null;
|
||||
if (match) {
|
||||
expect(actual).toEqual(status);
|
||||
} else {
|
||||
expect(actual).not.toEqual(status);
|
||||
}
|
||||
}
|
||||
|
||||
var assertPlaying = (player: MockAnimationPlayer, isPlaying: boolean) => {
|
||||
assertLastStatus(player, 'play', isPlaying);
|
||||
};
|
||||
|
||||
it('should pause/play the active player', () => {
|
||||
var sequence = new AnimationSequencePlayer(players);
|
||||
|
||||
assertPlaying(players[0], false);
|
||||
assertPlaying(players[1], false);
|
||||
assertPlaying(players[2], false);
|
||||
|
||||
sequence.play();
|
||||
|
||||
assertPlaying(players[0], true);
|
||||
assertPlaying(players[1], false);
|
||||
assertPlaying(players[2], false);
|
||||
|
||||
sequence.pause();
|
||||
|
||||
assertPlaying(players[0], false);
|
||||
assertPlaying(players[1], false);
|
||||
assertPlaying(players[2], false);
|
||||
|
||||
sequence.play();
|
||||
players[0].finish();
|
||||
|
||||
assertPlaying(players[0], false);
|
||||
assertPlaying(players[1], true);
|
||||
assertPlaying(players[2], false);
|
||||
|
||||
players[1].finish();
|
||||
|
||||
assertPlaying(players[0], false);
|
||||
assertPlaying(players[1], false);
|
||||
assertPlaying(players[2], true);
|
||||
|
||||
players[2].finish();
|
||||
sequence.pause();
|
||||
|
||||
assertPlaying(players[0], false);
|
||||
assertPlaying(players[1], false);
|
||||
assertPlaying(players[2], false);
|
||||
});
|
||||
|
||||
it('should finish when all players have finished', () => {
|
||||
var sequence = new AnimationSequencePlayer(players);
|
||||
|
||||
var completed = false;
|
||||
sequence.onDone(() => completed = true);
|
||||
sequence.play();
|
||||
|
||||
expect(completed).toBeFalsy();
|
||||
|
||||
players[0].finish();
|
||||
|
||||
expect(completed).toBeFalsy();
|
||||
|
||||
players[1].finish();
|
||||
|
||||
expect(completed).toBeFalsy();
|
||||
|
||||
players[2].finish();
|
||||
|
||||
expect(completed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should restart all the players', () => {
|
||||
var sequence = new AnimationSequencePlayer(players);
|
||||
|
||||
sequence.play();
|
||||
|
||||
assertPlaying(players[0], true);
|
||||
assertPlaying(players[1], false);
|
||||
assertPlaying(players[2], false);
|
||||
|
||||
players[0].finish();
|
||||
|
||||
assertPlaying(players[0], false);
|
||||
assertPlaying(players[1], true);
|
||||
assertPlaying(players[2], false);
|
||||
|
||||
sequence.restart();
|
||||
|
||||
assertLastStatus(players[0], 'restart', true);
|
||||
assertLastStatus(players[1], 'reset', true);
|
||||
assertLastStatus(players[2], 'reset', true);
|
||||
});
|
||||
|
||||
it('should finish all the players', () => {
|
||||
var sequence = new AnimationSequencePlayer(players);
|
||||
|
||||
var completed = false;
|
||||
sequence.onDone(() => completed = true);
|
||||
|
||||
sequence.play();
|
||||
|
||||
assertLastStatus(players[0], 'finish', false);
|
||||
assertLastStatus(players[1], 'finish', false);
|
||||
assertLastStatus(players[2], 'finish', false);
|
||||
|
||||
sequence.finish();
|
||||
|
||||
assertLastStatus(players[0], 'finish', true, -1);
|
||||
assertLastStatus(players[1], 'finish', true, -1);
|
||||
assertLastStatus(players[2], 'finish', true, -1);
|
||||
|
||||
assertLastStatus(players[0], 'destroy', true);
|
||||
assertLastStatus(players[1], 'destroy', true);
|
||||
assertLastStatus(players[2], 'destroy', true);
|
||||
|
||||
expect(completed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should call destroy automatically when finished if no parent player is present', () => {
|
||||
var sequence = new AnimationSequencePlayer(players);
|
||||
|
||||
sequence.play();
|
||||
|
||||
assertLastStatus(players[0], 'destroy', false);
|
||||
assertLastStatus(players[1], 'destroy', false);
|
||||
assertLastStatus(players[2], 'destroy', false);
|
||||
|
||||
sequence.finish();
|
||||
|
||||
assertLastStatus(players[0], 'destroy', true);
|
||||
assertLastStatus(players[1], 'destroy', true);
|
||||
assertLastStatus(players[2], 'destroy', true);
|
||||
});
|
||||
|
||||
it('should not call destroy automatically when finished if a parent player is present', () => {
|
||||
var sequence = new AnimationSequencePlayer(players);
|
||||
var parent = new AnimationSequencePlayer([sequence, new MockAnimationPlayer()]);
|
||||
|
||||
sequence.play();
|
||||
|
||||
assertLastStatus(players[0], 'destroy', false);
|
||||
assertLastStatus(players[1], 'destroy', false);
|
||||
assertLastStatus(players[2], 'destroy', false);
|
||||
|
||||
sequence.finish();
|
||||
|
||||
assertLastStatus(players[0], 'destroy', false);
|
||||
assertLastStatus(players[1], 'destroy', false);
|
||||
assertLastStatus(players[2], 'destroy', false);
|
||||
|
||||
parent.finish();
|
||||
|
||||
assertLastStatus(players[0], 'destroy', true);
|
||||
assertLastStatus(players[1], 'destroy', true);
|
||||
assertLastStatus(players[2], 'destroy', true);
|
||||
});
|
||||
|
||||
it('should function without any players', () => {
|
||||
var sequence = new AnimationSequencePlayer([]);
|
||||
sequence.onDone(() => {});
|
||||
sequence.pause();
|
||||
sequence.play();
|
||||
sequence.finish();
|
||||
sequence.restart();
|
||||
sequence.destroy();
|
||||
});
|
||||
|
||||
it('should call onDone after the next microtask if no players are provided', fakeAsync(() => {
|
||||
var sequence = new AnimationSequencePlayer([]);
|
||||
var completed = false;
|
||||
sequence.onDone(() => completed = true);
|
||||
expect(completed).toEqual(false);
|
||||
flushMicrotasks();
|
||||
expect(completed).toEqual(true);
|
||||
}));
|
||||
});
|
||||
}
|
@ -0,0 +1,195 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit
|
||||
} from '../../testing/testing_internal';
|
||||
|
||||
import {
|
||||
fakeAsync,
|
||||
flushMicrotasks
|
||||
} from '../../testing';
|
||||
|
||||
import {el} from '@angular/platform-browser/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {isPresent} from '../../src/facade/lang';
|
||||
import {MockAnimationPlayer} from '../../testing/animation/mock_animation_player';
|
||||
import {AnimationStyleUtil} from '../../src/animation/animation_style_util';
|
||||
import {AnimationKeyframe} from '../../src/animation/animation_keyframe';
|
||||
import {AnimationStyles} from '../../src/animation/animation_styles';
|
||||
|
||||
import {FILL_STYLE_FLAG} from '../../src/animation/animation_constants';
|
||||
import {AUTO_STYLE} from '../../src/animation/metadata';
|
||||
|
||||
export function main() {
|
||||
describe('AnimationStyleUtil', function() {
|
||||
|
||||
describe('balanceStyles', () => {
|
||||
it('should set all non-shared styles to the provided null value between the two sets of styles', () => {
|
||||
var styles = { opacity: 0, color: 'red' };
|
||||
var newStyles = { background: 'red' };
|
||||
var flag = '*';
|
||||
var result = AnimationStyleUtil.balanceStyles(styles, newStyles, flag);
|
||||
expect(result).toEqual({
|
||||
opacity:flag,
|
||||
color:flag,
|
||||
background:'red'
|
||||
})
|
||||
});
|
||||
|
||||
it('should handle an empty set of styles', () => {
|
||||
var value = '*';
|
||||
|
||||
expect(AnimationStyleUtil.balanceStyles({}, { opacity: 0 }, value)).toEqual({
|
||||
opacity: 0
|
||||
});
|
||||
|
||||
expect(AnimationStyleUtil.balanceStyles({ opacity: 0 }, {}, value)).toEqual({
|
||||
opacity: value
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('balanceKeyframes', () => {
|
||||
it('should balance both the starting and final keyframes with thep provided styles', () => {
|
||||
var collectedStyles = {
|
||||
width: 100,
|
||||
height: 200
|
||||
};
|
||||
|
||||
var finalStyles = {
|
||||
background: 'red',
|
||||
border: '1px solid black'
|
||||
};
|
||||
|
||||
var keyframes = [
|
||||
new AnimationKeyframe(0, new AnimationStyles([{ height: 100, opacity: 1 }])),
|
||||
new AnimationKeyframe(1, new AnimationStyles([{ background: 'blue', left: '100px', top: '100px' }]))
|
||||
];
|
||||
|
||||
var result = AnimationStyleUtil.balanceKeyframes(collectedStyles, finalStyles, keyframes);
|
||||
|
||||
expect(AnimationStyleUtil.flattenStyles(result[0].styles.styles)).toEqual({
|
||||
"width": 100,
|
||||
"height": 100,
|
||||
"opacity": 1,
|
||||
"background": '*',
|
||||
"border": '*',
|
||||
"left": '*',
|
||||
"top": '*'
|
||||
});
|
||||
|
||||
expect(AnimationStyleUtil.flattenStyles(result[1].styles.styles)).toEqual({
|
||||
"width": '*',
|
||||
"height": '*',
|
||||
"opacity": '*',
|
||||
"background": 'blue',
|
||||
"border": '1px solid black',
|
||||
"left": '100px',
|
||||
"top": '100px'
|
||||
});
|
||||
});
|
||||
|
||||
it('should perform balancing when no collected and final styles are provided', () => {
|
||||
var keyframes = [
|
||||
new AnimationKeyframe(0, new AnimationStyles([{ height: 100, opacity: 1 }])),
|
||||
new AnimationKeyframe(1, new AnimationStyles([{ width: 100 }]))
|
||||
];
|
||||
|
||||
var result = AnimationStyleUtil.balanceKeyframes({}, {}, keyframes);
|
||||
|
||||
expect(AnimationStyleUtil.flattenStyles(result[0].styles.styles)).toEqual({
|
||||
"height": 100,
|
||||
"opacity": 1,
|
||||
"width": "*"
|
||||
});
|
||||
|
||||
expect(AnimationStyleUtil.flattenStyles(result[1].styles.styles)).toEqual({
|
||||
"width": 100,
|
||||
"height": "*",
|
||||
"opacity": "*"
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('clearStyles', () => {
|
||||
it('should set all the style values to "null"', () => {
|
||||
var styles = {
|
||||
"opacity": 0,
|
||||
"width": 100,
|
||||
"color": "red"
|
||||
};
|
||||
var expectedResult = {
|
||||
"opacity": null,
|
||||
"width": null,
|
||||
"color": null
|
||||
};
|
||||
expect(AnimationStyleUtil.clearStyles(styles)).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should handle an empty set of styles', () => {
|
||||
expect(AnimationStyleUtil.clearStyles({})).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('collectAndResolveStyles', () => {
|
||||
it('should keep a record of the styles as they are called', () => {
|
||||
var styles1 = [{
|
||||
"opacity": 0,
|
||||
"width": 100
|
||||
}];
|
||||
|
||||
var styles2 = [{
|
||||
"height": 999,
|
||||
"opacity": 1
|
||||
}];
|
||||
|
||||
var collection: {[key: string]: string|number} = {};
|
||||
|
||||
expect(AnimationStyleUtil.collectAndResolveStyles(collection, styles1)).toEqual(styles1);
|
||||
expect(collection).toEqual({
|
||||
"opacity": 0,
|
||||
"width": 100
|
||||
});
|
||||
|
||||
expect(AnimationStyleUtil.collectAndResolveStyles(collection, styles2)).toEqual(styles2);
|
||||
expect(collection).toEqual({
|
||||
"opacity": 1,
|
||||
"width": 100,
|
||||
"height": 999
|
||||
});
|
||||
});
|
||||
|
||||
it('should resolve styles if they contain a FILL_STYLE_FLAG value', () => {
|
||||
var styles1 = [{
|
||||
"opacity": 0,
|
||||
"width": FILL_STYLE_FLAG
|
||||
}];
|
||||
|
||||
var styles2 = [{
|
||||
"height": 999,
|
||||
"opacity": FILL_STYLE_FLAG
|
||||
}];
|
||||
|
||||
var collection = {};
|
||||
|
||||
expect(AnimationStyleUtil.collectAndResolveStyles(collection, styles1)).toEqual([{
|
||||
"opacity": 0,
|
||||
"width": AUTO_STYLE
|
||||
}]);
|
||||
|
||||
expect(AnimationStyleUtil.collectAndResolveStyles(collection, styles2)).toEqual([{
|
||||
"opacity": 0,
|
||||
"height": 999
|
||||
}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import {AnimationDriver} from '../../src/animation/animation_driver';
|
||||
import {AnimationKeyframe} from '../../src/animation/animation_keyframe';
|
||||
import {AnimationPlayer} from '../../src/animation/animation_player';
|
||||
import {StringMapWrapper} from '../../src/facade/collection';
|
||||
import {AnimationStyles} from '../../src/animation/animation_styles';
|
||||
import {MockAnimationPlayer} from '../../testing/animation/mock_animation_player';
|
||||
|
||||
export class MockAnimationDriver extends AnimationDriver {
|
||||
log = [];
|
||||
animate(element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[], duration: number, delay: number,
|
||||
easing: string): AnimationPlayer {
|
||||
var player = new MockAnimationPlayer();
|
||||
this.log.push({
|
||||
'element': element,
|
||||
'startingStyles': _serializeStyles(startingStyles),
|
||||
'keyframes': keyframes,
|
||||
'keyframeLookup': _serializeKeyframes(keyframes),
|
||||
'duration': duration,
|
||||
'delay': delay,
|
||||
'easing': easing,
|
||||
'player': player
|
||||
});
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
||||
function _serializeKeyframes(keyframes: AnimationKeyframe[]): any[] {
|
||||
return keyframes.map(keyframe => [keyframe.offset, _serializeStyles(keyframe.styles)]);
|
||||
}
|
||||
|
||||
function _serializeStyles(styles: AnimationStyles): {[key: string]: any} {
|
||||
var flatStyles = {};
|
||||
styles.styles.forEach(entry => StringMapWrapper.forEach(entry, (val, prop) => { flatStyles[prop] = val; }));
|
||||
return flatStyles;
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import {isPresent} from '../../src/facade/lang';
|
||||
import {AnimationPlayer} from '../../src/animation/animation_player';
|
||||
|
||||
export class MockAnimationPlayer implements AnimationPlayer {
|
||||
private _subscriptions = [];
|
||||
private _finished = false;
|
||||
private _destroyed = false;
|
||||
public parentPlayer: AnimationPlayer = null;
|
||||
|
||||
public log = [];
|
||||
|
||||
private _onfinish(): void {
|
||||
if (!this._finished) {
|
||||
this._finished = true;
|
||||
this.log.push('finish');
|
||||
|
||||
this._subscriptions.forEach((entry) => { entry(); });
|
||||
this._subscriptions = [];
|
||||
if (!isPresent(this.parentPlayer)) {
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDone(fn: Function): void { this._subscriptions.push(fn); }
|
||||
|
||||
play(): void { this.log.push('play'); }
|
||||
|
||||
pause(): void { this.log.push('pause'); }
|
||||
|
||||
restart(): void { this.log.push('restart'); }
|
||||
|
||||
finish(): void { this._onfinish(); }
|
||||
|
||||
reset(): void { this.log.push('reset'); }
|
||||
|
||||
destroy(): void {
|
||||
if (!this._destroyed) {
|
||||
this._destroyed = true;
|
||||
this.finish();
|
||||
this.log.push('destroy');
|
||||
}
|
||||
}
|
||||
|
||||
setPosition(p): void {}
|
||||
getPosition(): number { return 0; }
|
||||
}
|
Reference in New Issue
Block a user