feat(core): introduce support for animations

Closes #8734
This commit is contained in:
Matias Niemelä
2016-05-25 12:46:22 -07:00
parent 6c6b316bd9
commit 5e0f8cf3f0
83 changed files with 5294 additions and 756 deletions

View File

@ -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);
}
}
}
}

View File

@ -0,0 +1,3 @@
export const FILL_STYLE_FLAG = 'true'; // TODO (matsko): change to boolean
export const ANY_STATE = '*';
export const EMPTY_STATE = 'void';

View 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();
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,5 @@
import {AnimationStyles} from './animation_styles';
export class AnimationKeyframe {
constructor(public offset: number, public styles: AnimationStyles) {}
}

View 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; }
}

View File

@ -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();
}
}

View 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;
}
}

View File

@ -0,0 +1,3 @@
export class AnimationStyles {
constructor(public styles: {[key: string]: string | number}[]) {}
}

View 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);
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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 {

View File

@ -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);
}
/**

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
/**