diff --git a/packages/animations/browser/src/dsl/animation.ts b/packages/animations/browser/src/dsl/animation.ts index f03f9c697c..f0948a1c4d 100644 --- a/packages/animations/browser/src/dsl/animation.ts +++ b/packages/animations/browser/src/dsl/animation.ts @@ -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; constructor(private _driver: AnimationDriver, input: AnimationMetadata|AnimationMetadata[]) { const errors: any[] = []; const ast = buildAnimationAst(_driver, input, errors); diff --git a/packages/animations/browser/src/dsl/animation_ast.ts b/packages/animations/browser/src/dsl/animation_ast.ts index 0a8b1309cd..7055b59542 100644 --- a/packages/animations/browser/src/dsl/animation_ast.ts +++ b/packages/animations/browser/src/dsl/animation_ast.ts @@ -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 { + 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 { + 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 { + 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 { + matchers: ((fromState: string, toState: string) => boolean)[]; + animation: Ast; + 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 { + steps: Ast[]; } -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 { + steps: Ast[]; } -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 { + 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 { + 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 { styles: StyleAst[]; } - visit(visitor: AstVisitor, context: any): any { return visitor.visitKeyframes(this, context); } +export interface ReferenceAst extends Ast { + animation: Ast; } -export class ReferenceAst extends Ast { - constructor(public animation: Ast) { super(); } +export interface AnimateChildAst extends Ast {} - visit(visitor: AstVisitor, context: any): any { return visitor.visitReference(this, context); } +export interface AnimateRefAst extends Ast { + 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 { + selector: string; + limit: number; + optional: boolean; + includeSelf: boolean; + animation: Ast; + 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 { + timings: AnimateTimings; + animation: Ast; } -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; } diff --git a/packages/animations/browser/src/dsl/animation_ast_builder.ts b/packages/animations/browser/src/dsl/animation_ast_builder.ts index 0a6fd2fd6e..958232d88c 100644 --- a/packages/animations/browser/src/dsl/animation_ast_builder.ts +++ b/packages/animations/browser/src/dsl/animation_ast_builder.ts @@ -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 { 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 { const context = new AnimationAstBuilderContext(errors); this._resetContextStyleTimingState(context); - return visitAnimationNode(this, normalizeAnimationEntry(metadata), context) as Ast; + return >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}; +} diff --git a/packages/animations/browser/src/dsl/animation_dsl_visitor.ts b/packages/animations/browser/src/dsl/animation_dsl_visitor.ts index 6b2176d0ab..e77f9db90e 100644 --- a/packages/animations/browser/src/dsl/animation_dsl_visitor.ts +++ b/packages/animations/browser/src/dsl/animation_dsl_visitor.ts @@ -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; } diff --git a/packages/animations/browser/src/dsl/animation_timeline_builder.ts b/packages/animations/browser/src/dsl/animation_timeline_builder.ts index 51cd604c22..5bfedae0f5 100644 --- a/packages/animations/browser/src/dsl/animation_timeline_builder.ts +++ b/packages/animations/browser/src/dsl/animation_timeline_builder.ts @@ -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, + 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, + 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 = {}; +const DEFAULT_NOOP_PREVIOUS_NODE = >{}; 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 = 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; diff --git a/packages/animations/browser/src/dsl/animation_trigger.ts b/packages/animations/browser/src/dsl/animation_trigger.ts index 9d820e25c2..b52ff0b5dc 100644 --- a/packages/animations/browser/src/dsl/animation_trigger.ts +++ b/packages/animations/browser/src/dsl/animation_trigger.ts @@ -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); } diff --git a/packages/animations/browser/src/render/timeline_animation_engine.ts b/packages/animations/browser/src/render/timeline_animation_engine.ts index b217b9f1cf..49fa5bc39c 100644 --- a/packages/animations/browser/src/render/timeline_animation_engine.ts +++ b/packages/animations/browser/src/render/timeline_animation_engine.ts @@ -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} = {}; private _playersById: {[id: string]: AnimationPlayer} = {}; public players: AnimationPlayer[] = []; diff --git a/packages/animations/browser/src/util.ts b/packages/animations/browser/src/util.ts index dbdfa4f213..20fd973191 100644 --- a/packages/animations/browser/src/util.ts +++ b/packages/animations/browser/src/util.ts @@ -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, 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}`); + } +}