perf(animations): reduce size of bundle by removing AST classes (#19539)

This CL refactors the animation AST code to make use of interfaces instead of classes. Given that interfaces are not persisted during runtime the removal of classes should nicely cut down on size for the animations-browser bundle.

-- before --
animations-browser.umd.js = 222kb
animations-browser.umd.min.js = 107kb

-- after --
animations-browser.umd.js = 213kb
animations-browser.umd.min.js = 102kb

PR Close #19539
This commit is contained in:
Matias Niemelä 2017-10-03 13:41:52 -07:00 committed by Chuck Jazdzewski
parent c4704c8abc
commit c3a52697f5
8 changed files with 254 additions and 239 deletions

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AnimationMetadata, AnimationOptions, ɵStyleData} from '@angular/animations';
import {AnimationMetadata, AnimationMetadataType, AnimationOptions, ɵStyleData} from '@angular/animations';
import {AnimationDriver} from '../render/animation_driver';
import {normalizeStyles} from '../util';
@ -17,7 +17,7 @@ import {AnimationTimelineInstruction} from './animation_timeline_instruction';
import {ElementInstructionMap} from './element_instruction_map';
export class Animation {
private _animationAst: Ast;
private _animationAst: Ast<AnimationMetadataType>;
constructor(private _driver: AnimationDriver, input: AnimationMetadata|AnimationMetadata[]) {
const errors: any[] = [];
const ast = buildAnimationAst(_driver, input, errors);

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AnimateTimings, AnimationOptions, ɵStyleData} from '@angular/animations';
import {AnimateTimings, AnimationMetadataType, AnimationOptions, ɵStyleData} from '@angular/animations';
const EMPTY_ANIMATION_OPTIONS: AnimationOptions = {};
@ -23,129 +23,90 @@ export interface AstVisitor {
visitAnimateRef(ast: AnimateRefAst, context: any): any;
visitQuery(ast: QueryAst, context: any): any;
visitStagger(ast: StaggerAst, context: any): any;
visitTiming(ast: TimingAst, context: any): any;
}
export abstract class Ast {
abstract visit(ast: AstVisitor, context: any): any;
public options: AnimationOptions = EMPTY_ANIMATION_OPTIONS;
get params(): {[name: string]: any}|null { return this.options['params'] || null; }
export interface Ast<T extends AnimationMetadataType> {
type: T;
options: AnimationOptions|null;
}
export class TriggerAst extends Ast {
public queryCount: number = 0;
public depCount: number = 0;
constructor(public name: string, public states: StateAst[], public transitions: TransitionAst[]) {
super();
}
visit(visitor: AstVisitor, context: any): any { return visitor.visitTrigger(this, context); }
export interface TriggerAst extends Ast<AnimationMetadataType.Trigger> {
type: AnimationMetadataType.Trigger;
name: string;
states: StateAst[];
transitions: TransitionAst[];
queryCount: number;
depCount: number;
}
export class StateAst extends Ast {
constructor(public name: string, public style: StyleAst) { super(); }
visit(visitor: AstVisitor, context: any): any { return visitor.visitState(this, context); }
export interface StateAst extends Ast<AnimationMetadataType.State> {
type: AnimationMetadataType.State;
name: string;
style: StyleAst;
}
export class TransitionAst extends Ast {
public queryCount: number = 0;
public depCount: number = 0;
constructor(
public matchers: ((fromState: string, toState: string) => boolean)[], public animation: Ast) {
super();
}
visit(visitor: AstVisitor, context: any): any { return visitor.visitTransition(this, context); }
export interface TransitionAst extends Ast<AnimationMetadataType.Transition> {
matchers: ((fromState: string, toState: string) => boolean)[];
animation: Ast<AnimationMetadataType>;
queryCount: number;
depCount: number;
}
export class SequenceAst extends Ast {
constructor(public steps: Ast[]) { super(); }
visit(visitor: AstVisitor, context: any): any { return visitor.visitSequence(this, context); }
export interface SequenceAst extends Ast<AnimationMetadataType.Sequence> {
steps: Ast<AnimationMetadataType>[];
}
export class GroupAst extends Ast {
constructor(public steps: Ast[]) { super(); }
visit(visitor: AstVisitor, context: any): any { return visitor.visitGroup(this, context); }
export interface GroupAst extends Ast<AnimationMetadataType.Group> {
steps: Ast<AnimationMetadataType>[];
}
export class AnimateAst extends Ast {
constructor(public timings: TimingAst, public style: StyleAst|KeyframesAst) { super(); }
visit(visitor: AstVisitor, context: any): any { return visitor.visitAnimate(this, context); }
export interface AnimateAst extends Ast<AnimationMetadataType.Animate> {
timings: TimingAst;
style: StyleAst|KeyframesAst;
}
export class StyleAst extends Ast {
public isEmptyStep = false;
public containsDynamicStyles = false;
constructor(
public styles: (ɵStyleData|string)[], public easing: string|null,
public offset: number|null) {
super();
}
visit(visitor: AstVisitor, context: any): any { return visitor.visitStyle(this, context); }
export interface StyleAst extends Ast<AnimationMetadataType.Style> {
styles: (ɵStyleData|string)[];
easing: string|null;
offset: number|null;
containsDynamicStyles: boolean;
isEmptyStep?: boolean;
}
export class KeyframesAst extends Ast {
constructor(public styles: StyleAst[]) { super(); }
export interface KeyframesAst extends Ast<AnimationMetadataType.Keyframes> { styles: StyleAst[]; }
visit(visitor: AstVisitor, context: any): any { return visitor.visitKeyframes(this, context); }
export interface ReferenceAst extends Ast<AnimationMetadataType.Reference> {
animation: Ast<AnimationMetadataType>;
}
export class ReferenceAst extends Ast {
constructor(public animation: Ast) { super(); }
export interface AnimateChildAst extends Ast<AnimationMetadataType.AnimateChild> {}
visit(visitor: AstVisitor, context: any): any { return visitor.visitReference(this, context); }
export interface AnimateRefAst extends Ast<AnimationMetadataType.AnimateRef> {
animation: ReferenceAst;
}
export class AnimateChildAst extends Ast {
constructor() { super(); }
visit(visitor: AstVisitor, context: any): any { return visitor.visitAnimateChild(this, context); }
export interface QueryAst extends Ast<AnimationMetadataType.Query> {
selector: string;
limit: number;
optional: boolean;
includeSelf: boolean;
animation: Ast<AnimationMetadataType>;
originalSelector: string;
}
export class AnimateRefAst extends Ast {
constructor(public animation: ReferenceAst) { super(); }
visit(visitor: AstVisitor, context: any): any { return visitor.visitAnimateRef(this, context); }
export interface StaggerAst extends Ast<AnimationMetadataType.Stagger> {
timings: AnimateTimings;
animation: Ast<AnimationMetadataType>;
}
export class QueryAst extends Ast {
public originalSelector: string;
constructor(
public selector: string, public limit: number, public optional: boolean,
public includeSelf: boolean, public animation: Ast) {
super();
}
visit(visitor: AstVisitor, context: any): any { return visitor.visitQuery(this, context); }
export interface TimingAst {
duration: number;
delay: number;
easing: string|null;
dynamic?: boolean;
}
export class StaggerAst extends Ast {
constructor(public timings: AnimateTimings, public animation: Ast) { super(); }
visit(visitor: AstVisitor, context: any): any { return visitor.visitStagger(this, context); }
}
export class TimingAst extends Ast {
constructor(
public duration: number, public delay: number = 0, public easing: string|null = null) {
super();
}
visit(visitor: AstVisitor, context: any): any { return visitor.visitTiming(this, context); }
}
export class DynamicTimingAst extends TimingAst {
constructor(public value: string) { super(0, 0, ''); }
visit(visitor: AstVisitor, context: any): any { return visitor.visitTiming(this, context); }
export interface DynamicTimingAst extends TimingAst {
strValue: string;
dynamic: true;
}

View File

@ -9,10 +9,10 @@ import {AUTO_STYLE, AnimateTimings, AnimationAnimateChildMetadata, AnimationAnim
import {AnimationDriver} from '../render/animation_driver';
import {getOrSetAsInMap} from '../render/shared';
import {ENTER_SELECTOR, LEAVE_SELECTOR, NG_ANIMATING_SELECTOR, NG_TRIGGER_SELECTOR, SUBSTITUTION_EXPR_START, copyObj, extractStyleParams, iteratorToArray, normalizeAnimationEntry, resolveTiming, validateStyleParams} from '../util';
import {ENTER_SELECTOR, LEAVE_SELECTOR, NG_ANIMATING_SELECTOR, NG_TRIGGER_SELECTOR, SUBSTITUTION_EXPR_START, copyObj, extractStyleParams, iteratorToArray, normalizeAnimationEntry, resolveTiming, validateStyleParams, visitDslNode} from '../util';
import {AnimateAst, AnimateChildAst, AnimateRefAst, Ast, DynamicTimingAst, GroupAst, KeyframesAst, QueryAst, ReferenceAst, SequenceAst, StaggerAst, StateAst, StyleAst, TimingAst, TransitionAst, TriggerAst} from './animation_ast';
import {AnimationDslVisitor, visitAnimationNode} from './animation_dsl_visitor';
import {AnimationDslVisitor} from './animation_dsl_visitor';
import {parseTransitionExpr} from './animation_transition_expr';
const SELF_TOKEN = ':self';
@ -56,7 +56,7 @@ const SELF_TOKEN_REGEX = new RegExp(`\s*${SELF_TOKEN}\s*,?`, 'g');
*/
export function buildAnimationAst(
driver: AnimationDriver, metadata: AnimationMetadata | AnimationMetadata[],
errors: any[]): Ast {
errors: any[]): Ast<AnimationMetadataType> {
return new AnimationAstBuilderVisitor(driver).build(metadata, errors);
}
@ -69,10 +69,12 @@ const ROOT_SELECTOR = '';
export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
constructor(private _driver: AnimationDriver) {}
build(metadata: AnimationMetadata|AnimationMetadata[], errors: any[]): Ast {
build(metadata: AnimationMetadata|AnimationMetadata[], errors: any[]):
Ast<AnimationMetadataType> {
const context = new AnimationAstBuilderContext(errors);
this._resetContextStyleTimingState(context);
return visitAnimationNode(this, normalizeAnimationEntry(metadata), context) as Ast;
return <Ast<AnimationMetadataType>>visitDslNode(
this, normalizeAnimationEntry(metadata), context);
}
private _resetContextStyleTimingState(context: AnimationAstBuilderContext) {
@ -108,11 +110,12 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
'only state() and transition() definitions can sit inside of a trigger()');
}
});
const ast = new TriggerAst(metadata.name, states, transitions);
ast.options = normalizeAnimationOptions(metadata.options);
ast.queryCount = queryCount;
ast.depCount = depCount;
return ast;
return {
type: AnimationMetadataType.Trigger,
name: metadata.name, states, transitions, queryCount, depCount,
options: null
};
}
visitState(metadata: AnimationStateMetadata, context: AnimationAstBuilderContext): StateAst {
@ -140,31 +143,38 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
}
}
const stateAst = new StateAst(metadata.name, styleAst);
if (astParams) {
stateAst.options = {params: astParams};
}
return stateAst;
return {
type: AnimationMetadataType.State,
name: metadata.name,
style: styleAst,
options: astParams ? {params: astParams} : null
};
}
visitTransition(metadata: AnimationTransitionMetadata, context: AnimationAstBuilderContext):
TransitionAst {
context.queryCount = 0;
context.depCount = 0;
const entry = visitAnimationNode(this, normalizeAnimationEntry(metadata.animation), context);
const animation = visitDslNode(this, normalizeAnimationEntry(metadata.animation), context);
const matchers = parseTransitionExpr(metadata.expr, context.errors);
const ast = new TransitionAst(matchers, entry);
ast.options = normalizeAnimationOptions(metadata.options);
ast.queryCount = context.queryCount;
ast.depCount = context.depCount;
return ast;
return {
type: AnimationMetadataType.Transition,
matchers,
animation,
queryCount: context.queryCount,
depCount: context.depCount,
options: normalizeAnimationOptions(metadata.options)
};
}
visitSequence(metadata: AnimationSequenceMetadata, context: AnimationAstBuilderContext):
SequenceAst {
const ast = new SequenceAst(metadata.steps.map(s => visitAnimationNode(this, s, context)));
ast.options = normalizeAnimationOptions(metadata.options);
return ast;
return {
type: AnimationMetadataType.Sequence,
steps: metadata.steps.map(s => visitDslNode(this, s, context)),
options: normalizeAnimationOptions(metadata.options)
};
}
visitGroup(metadata: AnimationGroupMetadata, context: AnimationAstBuilderContext): GroupAst {
@ -172,15 +182,17 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
let furthestTime = 0;
const steps = metadata.steps.map(step => {
context.currentTime = currentTime;
const innerAst = visitAnimationNode(this, step, context);
const innerAst = visitDslNode(this, step, context);
furthestTime = Math.max(furthestTime, context.currentTime);
return innerAst;
});
context.currentTime = furthestTime;
const ast = new GroupAst(steps);
ast.options = normalizeAnimationOptions(metadata.options);
return ast;
return {
type: AnimationMetadataType.Group,
steps,
options: normalizeAnimationOptions(metadata.options)
};
}
visitAnimate(metadata: AnimationAnimateMetadata, context: AnimationAstBuilderContext):
@ -188,10 +200,10 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
const timingAst = constructTimingAst(metadata.timings, context.errors);
context.currentAnimateTimings = timingAst;
let styles: StyleAst|KeyframesAst;
let styleAst: StyleAst|KeyframesAst;
let styleMetadata: AnimationMetadata = metadata.styles ? metadata.styles : style({});
if (styleMetadata.type == AnimationMetadataType.Keyframes) {
styles = this.visitKeyframes(styleMetadata as AnimationKeyframesSequenceMetadata, context);
styleAst = this.visitKeyframes(styleMetadata as AnimationKeyframesSequenceMetadata, context);
} else {
let styleMetadata = metadata.styles as AnimationStyleMetadata;
let isEmpty = false;
@ -204,13 +216,18 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
styleMetadata = style(newStyleData);
}
context.currentTime += timingAst.duration + timingAst.delay;
const styleAst = this.visitStyle(styleMetadata, context);
styleAst.isEmptyStep = isEmpty;
styles = styleAst;
const _styleAst = this.visitStyle(styleMetadata, context);
_styleAst.isEmptyStep = isEmpty;
styleAst = _styleAst;
}
context.currentAnimateTimings = null;
return new AnimateAst(timingAst, styles);
return {
type: AnimationMetadataType.Animate,
timings: timingAst,
style: styleAst,
options: null
};
}
visitStyle(metadata: AnimationStyleMetadata, context: AnimationAstBuilderContext): StyleAst {
@ -260,9 +277,13 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
}
});
const ast = new StyleAst(styles, collectedEasing, metadata.offset);
ast.containsDynamicStyles = containsDynamicStyles;
return ast;
return {
type: AnimationMetadataType.Style,
styles,
easing: collectedEasing,
offset: metadata.offset, containsDynamicStyles,
options: null
};
}
private _validateStyleAst(ast: StyleAst, context: AnimationAstBuilderContext): void {
@ -313,9 +334,10 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
visitKeyframes(metadata: AnimationKeyframesSequenceMetadata, context: AnimationAstBuilderContext):
KeyframesAst {
const ast: KeyframesAst = {type: AnimationMetadataType.Keyframes, styles: [], options: null};
if (!context.currentAnimateTimings) {
context.errors.push(`keyframes() must be placed inside of a call to animate()`);
return new KeyframesAst([]);
return ast;
}
const MAX_KEYFRAME_OFFSET = 1;
@ -369,33 +391,38 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
currentAnimateTimings.duration = durationUpToThisFrame;
this._validateStyleAst(kf, context);
kf.offset = offset;
ast.styles.push(kf);
});
return new KeyframesAst(keyframes);
return ast;
}
visitReference(metadata: AnimationReferenceMetadata, context: AnimationAstBuilderContext):
ReferenceAst {
const entry = visitAnimationNode(this, normalizeAnimationEntry(metadata.animation), context);
const ast = new ReferenceAst(entry);
ast.options = normalizeAnimationOptions(metadata.options);
return ast;
return {
type: AnimationMetadataType.Reference,
animation: visitDslNode(this, normalizeAnimationEntry(metadata.animation), context),
options: normalizeAnimationOptions(metadata.options)
};
}
visitAnimateChild(metadata: AnimationAnimateChildMetadata, context: AnimationAstBuilderContext):
AnimateChildAst {
context.depCount++;
const ast = new AnimateChildAst();
ast.options = normalizeAnimationOptions(metadata.options);
return ast;
return {
type: AnimationMetadataType.AnimateChild,
options: normalizeAnimationOptions(metadata.options)
};
}
visitAnimateRef(metadata: AnimationAnimateRefMetadata, context: AnimationAstBuilderContext):
AnimateRefAst {
const animation = this.visitReference(metadata.animation, context);
const ast = new AnimateRefAst(animation);
ast.options = normalizeAnimationOptions(metadata.options);
return ast;
return {
type: AnimationMetadataType.AnimateRef,
animation: this.visitReference(metadata.animation, context),
options: normalizeAnimationOptions(metadata.options)
};
}
visitQuery(metadata: AnimationQueryMetadata, context: AnimationAstBuilderContext): QueryAst {
@ -409,14 +436,18 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
parentSelector.length ? (parentSelector + ' ' + selector) : selector;
getOrSetAsInMap(context.collectedStyles, context.currentQuerySelector, {});
const entry = visitAnimationNode(this, normalizeAnimationEntry(metadata.animation), context);
const animation = visitDslNode(this, normalizeAnimationEntry(metadata.animation), context);
context.currentQuery = null;
context.currentQuerySelector = parentSelector;
const ast = new QueryAst(selector, options.limit || 0, !!options.optional, includeSelf, entry);
ast.originalSelector = metadata.selector;
ast.options = normalizeAnimationOptions(metadata.options);
return ast;
return {
type: AnimationMetadataType.Query,
selector,
limit: options.limit || 0,
optional: !!options.optional, includeSelf, animation,
originalSelector: metadata.selector,
options: normalizeAnimationOptions(metadata.options)
};
}
visitStagger(metadata: AnimationStaggerMetadata, context: AnimationAstBuilderContext):
@ -427,9 +458,12 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
const timings = metadata.timings === 'full' ?
{duration: 0, delay: 0, easing: 'full'} :
resolveTiming(metadata.timings, context.errors, true);
const animation =
visitAnimationNode(this, normalizeAnimationEntry(metadata.animation), context);
return new StaggerAst(timings, animation);
return {
type: AnimationMetadataType.Stagger,
animation: visitDslNode(this, normalizeAnimationEntry(metadata.animation), context), timings,
options: null
};
}
}
@ -501,17 +535,20 @@ function constructTimingAst(value: string | number | AnimateTimings, errors: any
timings = value as AnimateTimings;
} else if (typeof value == 'number') {
const duration = resolveTiming(value as number, errors).duration;
return new TimingAst(value as number, 0, '');
return makeTimingAst(duration as number, 0, '');
}
const strValue = value as string;
const isDynamic = strValue.split(/\s+/).some(v => v.charAt(0) == '{' && v.charAt(1) == '{');
if (isDynamic) {
return new DynamicTimingAst(strValue);
const ast = makeTimingAst(0, 0, '') as any;
ast.dynamic = true;
ast.strValue = strValue;
return ast as DynamicTimingAst;
}
timings = timings || resolveTiming(strValue, errors);
return new TimingAst(timings.duration, timings.delay, timings.easing);
return makeTimingAst(timings.duration, timings.delay, timings.easing);
}
function normalizeAnimationOptions(options: AnimationOptions | null): AnimationOptions {
@ -525,3 +562,7 @@ function normalizeAnimationOptions(options: AnimationOptions | null): AnimationO
}
return options;
}
function makeTimingAst(duration: number, delay: number, easing: string | null): TimingAst {
return {duration, delay, easing};
}

View File

@ -5,54 +5,20 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AnimationAnimateChildMetadata, AnimationAnimateMetadata, AnimationAnimateRefMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationMetadataType, AnimationQueryMetadata, AnimationReferenceMetadata, AnimationSequenceMetadata, AnimationStaggerMetadata, AnimationStateMetadata, AnimationStyleMetadata, AnimationTransitionMetadata, AnimationTriggerMetadata} from '@angular/animations';
import {AnimationAnimateChildMetadata, AnimationAnimateMetadata, AnimationAnimateRefMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationQueryMetadata, AnimationReferenceMetadata, AnimationSequenceMetadata, AnimationStaggerMetadata, AnimationStateMetadata, AnimationStyleMetadata, AnimationTransitionMetadata, AnimationTriggerMetadata} from '@angular/animations';
export interface AnimationDslVisitor {
visitTrigger(ast: AnimationTriggerMetadata, context: any): any;
visitState(ast: AnimationStateMetadata, context: any): any;
visitTransition(ast: AnimationTransitionMetadata, context: any): any;
visitSequence(ast: AnimationSequenceMetadata, context: any): any;
visitGroup(ast: AnimationGroupMetadata, context: any): any;
visitAnimate(ast: AnimationAnimateMetadata, context: any): any;
visitStyle(ast: AnimationStyleMetadata, context: any): any;
visitKeyframes(ast: AnimationKeyframesSequenceMetadata, context: any): any;
visitReference(ast: AnimationReferenceMetadata, context: any): any;
visitAnimateChild(ast: AnimationAnimateChildMetadata, context: any): any;
visitAnimateRef(ast: AnimationAnimateRefMetadata, context: any): any;
visitQuery(ast: AnimationQueryMetadata, context: any): any;
visitStagger(ast: AnimationStaggerMetadata, context: any): any;
}
export function visitAnimationNode(
visitor: AnimationDslVisitor, node: AnimationMetadata, context: any) {
switch (node.type) {
case AnimationMetadataType.Trigger:
return visitor.visitTrigger(node as AnimationTriggerMetadata, context);
case AnimationMetadataType.State:
return visitor.visitState(node as AnimationStateMetadata, context);
case AnimationMetadataType.Transition:
return visitor.visitTransition(node as AnimationTransitionMetadata, context);
case AnimationMetadataType.Sequence:
return visitor.visitSequence(node as AnimationSequenceMetadata, context);
case AnimationMetadataType.Group:
return visitor.visitGroup(node as AnimationGroupMetadata, context);
case AnimationMetadataType.Animate:
return visitor.visitAnimate(node as AnimationAnimateMetadata, context);
case AnimationMetadataType.Keyframes:
return visitor.visitKeyframes(node as AnimationKeyframesSequenceMetadata, context);
case AnimationMetadataType.Style:
return visitor.visitStyle(node as AnimationStyleMetadata, context);
case AnimationMetadataType.Reference:
return visitor.visitReference(node as AnimationReferenceMetadata, context);
case AnimationMetadataType.AnimateChild:
return visitor.visitAnimateChild(node as AnimationAnimateChildMetadata, context);
case AnimationMetadataType.AnimateRef:
return visitor.visitAnimateRef(node as AnimationAnimateRefMetadata, context);
case AnimationMetadataType.Query:
return visitor.visitQuery(node as AnimationQueryMetadata, context);
case AnimationMetadataType.Stagger:
return visitor.visitStagger(node as AnimationStaggerMetadata, context);
default:
throw new Error(`Unable to resolve animation metadata node #${node.type}`);
}
visitTrigger(node: AnimationTriggerMetadata, context: any): any;
visitState(node: AnimationStateMetadata, context: any): any;
visitTransition(node: AnimationTransitionMetadata, context: any): any;
visitSequence(node: AnimationSequenceMetadata, context: any): any;
visitGroup(node: AnimationGroupMetadata, context: any): any;
visitAnimate(node: AnimationAnimateMetadata, context: any): any;
visitStyle(node: AnimationStyleMetadata, context: any): any;
visitKeyframes(node: AnimationKeyframesSequenceMetadata, context: any): any;
visitReference(node: AnimationReferenceMetadata, context: any): any;
visitAnimateChild(node: AnimationAnimateChildMetadata, context: any): any;
visitAnimateRef(node: AnimationAnimateRefMetadata, context: any): any;
visitQuery(node: AnimationQueryMetadata, context: any): any;
visitStagger(node: AnimationStaggerMetadata, context: any): any;
}

View File

@ -5,10 +5,10 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AUTO_STYLE, AnimateChildOptions, AnimateTimings, AnimationOptions, AnimationQueryOptions, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations';
import {AUTO_STYLE, AnimateChildOptions, AnimateTimings, AnimationMetadataType, AnimationOptions, AnimationQueryOptions, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations';
import {AnimationDriver} from '../render/animation_driver';
import {copyObj, copyStyles, interpolateParams, iteratorToArray, resolveTiming, resolveTimingValue} from '../util';
import {copyObj, copyStyles, interpolateParams, iteratorToArray, resolveTiming, resolveTimingValue, visitDslNode} from '../util';
import {AnimateAst, AnimateChildAst, AnimateRefAst, Ast, AstVisitor, DynamicTimingAst, GroupAst, KeyframesAst, QueryAst, ReferenceAst, SequenceAst, StaggerAst, StateAst, StyleAst, TimingAst, TransitionAst, TriggerAst} from './animation_ast';
import {AnimationTimelineInstruction, createTimelineInstruction} from './animation_timeline_instruction';
@ -101,8 +101,8 @@ const ONE_FRAME_IN_MILLISECONDS = 1;
* the `AnimationValidatorVisitor` code.
*/
export function buildAnimationTimelines(
driver: AnimationDriver, rootElement: any, ast: Ast, startingStyles: ɵStyleData = {},
finalStyles: ɵStyleData = {}, options: AnimationOptions,
driver: AnimationDriver, rootElement: any, ast: Ast<AnimationMetadataType>,
startingStyles: ɵStyleData = {}, finalStyles: ɵStyleData = {}, options: AnimationOptions,
subInstructions?: ElementInstructionMap, errors: any[] = []): AnimationTimelineInstruction[] {
return new AnimationTimelineBuilderVisitor().buildKeyframes(
driver, rootElement, ast, startingStyles, finalStyles, options, subInstructions, errors);
@ -110,15 +110,15 @@ export function buildAnimationTimelines(
export class AnimationTimelineBuilderVisitor implements AstVisitor {
buildKeyframes(
driver: AnimationDriver, rootElement: any, ast: Ast, startingStyles: ɵStyleData,
finalStyles: ɵStyleData, options: AnimationOptions, subInstructions?: ElementInstructionMap,
errors: any[] = []): AnimationTimelineInstruction[] {
driver: AnimationDriver, rootElement: any, ast: Ast<AnimationMetadataType>,
startingStyles: ɵStyleData, finalStyles: ɵStyleData, options: AnimationOptions,
subInstructions?: ElementInstructionMap, errors: any[] = []): AnimationTimelineInstruction[] {
subInstructions = subInstructions || new ElementInstructionMap();
const context = new AnimationTimelineContext(driver, rootElement, subInstructions, errors, []);
context.options = options;
context.currentTimeline.setStyles([startingStyles], null, context.errors, options);
ast.visit(this, context);
visitDslNode(this, ast, context);
// this checks to see if an actual animation happened
const timelines = context.timelines.filter(timeline => timeline.containsAnimation());
@ -193,7 +193,7 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
visitReference(ast: ReferenceAst, context: AnimationTimelineContext) {
context.updateOptions(ast.options, true);
ast.animation.visit(this, context);
visitDslNode(this, ast.animation, context);
context.previousNode = ast;
}
@ -207,7 +207,7 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
ctx.transformIntoNewTimeline();
if (options.delay != null) {
if (ctx.previousNode instanceof StyleAst) {
if (ctx.previousNode.type == AnimationMetadataType.Style) {
ctx.currentTimeline.snapshotCurrentStyles();
ctx.previousNode = DEFAULT_NOOP_PREVIOUS_NODE;
}
@ -218,7 +218,7 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
}
if (ast.steps.length) {
ast.steps.forEach(s => s.visit(this, ctx));
ast.steps.forEach(s => visitDslNode(this, s, ctx));
// this is here just incase the inner steps only contain or end with a style() call
ctx.currentTimeline.applyStylesToKeyframe();
@ -245,7 +245,7 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
innerContext.delayNextStep(delay);
}
s.visit(this, innerContext);
visitDslNode(this, s, innerContext);
furthestTime = Math.max(furthestTime, innerContext.currentTimeline.currentTime);
innerTimelines.push(innerContext.currentTimeline);
});
@ -259,19 +259,19 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
context.previousNode = ast;
}
visitTiming(ast: TimingAst, context: AnimationTimelineContext): AnimateTimings {
if (ast instanceof DynamicTimingAst) {
const strValue = context.params ?
interpolateParams(ast.value, context.params, context.errors) :
ast.value.toString();
return resolveTiming(strValue, context.errors);
private _visitTiming(ast: TimingAst, context: AnimationTimelineContext): AnimateTimings {
if ((ast as DynamicTimingAst).dynamic) {
const strValue = (ast as DynamicTimingAst).strValue;
const timingValue =
context.params ? interpolateParams(strValue, context.params, context.errors) : strValue;
return resolveTiming(timingValue, context.errors);
} else {
return {duration: ast.duration, delay: ast.delay, easing: ast.easing};
}
}
visitAnimate(ast: AnimateAst, context: AnimationTimelineContext) {
const timings = context.currentAnimateTimings = this.visitTiming(ast.timings, context);
const timings = context.currentAnimateTimings = this._visitTiming(ast.timings, context);
const timeline = context.currentTimeline;
if (timings.delay) {
context.incrementTime(timings.delay);
@ -279,7 +279,7 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
}
const style = ast.style;
if (style instanceof KeyframesAst) {
if (style.type == AnimationMetadataType.Keyframes) {
this.visitKeyframes(style, context);
} else {
context.incrementTime(timings.duration);
@ -343,7 +343,7 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
const options = (ast.options || {}) as AnimationQueryOptions;
const delay = options.delay ? resolveTimingValue(options.delay) : 0;
if (delay && (context.previousNode instanceof StyleAst ||
if (delay && (context.previousNode.type === AnimationMetadataType.Style ||
(startTime == 0 && context.currentTimeline.getCurrentStyleProperties().length))) {
context.currentTimeline.snapshotCurrentStyles();
context.previousNode = DEFAULT_NOOP_PREVIOUS_NODE;
@ -368,7 +368,7 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
sameElementTimeline = innerContext.currentTimeline;
}
ast.animation.visit(this, innerContext);
visitDslNode(this, ast.animation, innerContext);
// this is here just incase the inner steps only contain or end
// with a style() call (which is here to signal that this is a preparatory
@ -415,7 +415,7 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
}
const startingTime = timeline.currentTime;
ast.animation.visit(this, context);
visitDslNode(this, ast.animation, context);
context.previousNode = ast;
// time = duration + delay
@ -431,12 +431,12 @@ export declare type StyleAtTime = {
time: number; value: string | number;
};
const DEFAULT_NOOP_PREVIOUS_NODE = <Ast>{};
const DEFAULT_NOOP_PREVIOUS_NODE = <Ast<AnimationMetadataType>>{};
export class AnimationTimelineContext {
public parentContext: AnimationTimelineContext|null = null;
public currentTimeline: TimelineBuilder;
public currentAnimateTimings: AnimateTimings|null = null;
public previousNode: Ast = DEFAULT_NOOP_PREVIOUS_NODE;
public previousNode: Ast<AnimationMetadataType> = DEFAULT_NOOP_PREVIOUS_NODE;
public subContextCount = 0;
public options: AnimationOptions = {};
public currentQueryIndex: number = 0;
@ -489,7 +489,7 @@ export class AnimationTimelineContext {
const oldParams = this.options.params;
if (oldParams) {
const params: {[name: string]: any} = options['params'] = {};
Object.keys(this.options.params).forEach(name => { params[name] = oldParams[name]; });
Object.keys(oldParams).forEach(name => { params[name] = oldParams[name]; });
}
}
return options;

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ɵStyleData} from '@angular/animations';
import {AnimationMetadataType, ɵStyleData} from '@angular/animations';
import {copyStyles, interpolateParams} from '../util';
@ -13,6 +13,7 @@ import {SequenceAst, StyleAst, TransitionAst, TriggerAst} from './animation_ast'
import {AnimationStateStyles, AnimationTransitionFactory} from './animation_transition_factory';
/**
* @experimental Animation support is experimental.
*/
@ -60,8 +61,15 @@ function createFallbackTransition(
triggerName: string,
states: {[stateName: string]: AnimationStateStyles}): AnimationTransitionFactory {
const matchers = [(fromState: any, toState: any) => true];
const animation = new SequenceAst([]);
const transition = new TransitionAst(matchers, animation);
const animation: SequenceAst = {type: AnimationMetadataType.Sequence, steps: [], options: null};
const transition: TransitionAst = {
type: AnimationMetadataType.Transition,
animation,
matchers,
options: null,
queryCount: 0,
depCount: 0
};
return new AnimationTransitionFactory(triggerName, transition, states);
}

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AUTO_STYLE, AnimationMetadata, AnimationOptions, AnimationPlayer, ɵStyleData} from '@angular/animations';
import {AUTO_STYLE, AnimationMetadata, AnimationMetadataType, AnimationOptions, AnimationPlayer, ɵStyleData} from '@angular/animations';
import {Ast} from '../dsl/animation_ast';
import {buildAnimationAst} from '../dsl/animation_ast_builder';
@ -20,7 +20,7 @@ import {getOrSetAsInMap, listenOnPlayer, makeAnimationEvent, normalizeKeyframes,
const EMPTY_INSTRUCTION_MAP = new ElementInstructionMap();
export class TimelineAnimationEngine {
private _animations: {[id: string]: Ast} = {};
private _animations: {[id: string]: Ast<AnimationMetadataType>} = {};
private _playersById: {[id: string]: AnimationPlayer} = {};
public players: AnimationPlayer[] = [];

View File

@ -5,7 +5,9 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AnimateTimings, AnimationMetadata, AnimationOptions, sequence, ɵStyleData} from '@angular/animations';
import {AnimateTimings, AnimationMetadata, AnimationMetadataType, AnimationOptions, sequence, ɵStyleData} from '@angular/animations';
import {Ast as AnimationAst, AstVisitor as AnimationAstVisitor} from './dsl/animation_ast';
import {AnimationDslVisitor} from './dsl/animation_dsl_visitor';
export const ONE_SECOND = 1000;
@ -232,3 +234,40 @@ export function dashCaseToCamelCase(input: string): string {
export function allowPreviousPlayerStylesMerge(duration: number, delay: number) {
return duration === 0 || delay === 0;
}
export function visitDslNode(
visitor: AnimationDslVisitor, node: AnimationMetadata, context: any): any;
export function visitDslNode(
visitor: AnimationAstVisitor, node: AnimationAst<AnimationMetadataType>, context: any): any;
export function visitDslNode(visitor: any, node: any, context: any): any {
switch (node.type) {
case AnimationMetadataType.Trigger:
return visitor.visitTrigger(node, context);
case AnimationMetadataType.State:
return visitor.visitState(node, context);
case AnimationMetadataType.Transition:
return visitor.visitTransition(node, context);
case AnimationMetadataType.Sequence:
return visitor.visitSequence(node, context);
case AnimationMetadataType.Group:
return visitor.visitGroup(node, context);
case AnimationMetadataType.Animate:
return visitor.visitAnimate(node, context);
case AnimationMetadataType.Keyframes:
return visitor.visitKeyframes(node, context);
case AnimationMetadataType.Style:
return visitor.visitStyle(node, context);
case AnimationMetadataType.Reference:
return visitor.visitReference(node, context);
case AnimationMetadataType.AnimateChild:
return visitor.visitAnimateChild(node, context);
case AnimationMetadataType.AnimateRef:
return visitor.visitAnimateRef(node, context);
case AnimationMetadataType.Query:
return visitor.visitQuery(node, context);
case AnimationMetadataType.Stagger:
return visitor.visitStagger(node, context);
default:
throw new Error(`Unable to resolve animation metadata node #${node.type}`);
}
}