@ -22,7 +22,7 @@ export class Animation {
|
||||
const errors: any[] = [];
|
||||
const ast = buildAnimationAst(_driver, input, errors);
|
||||
if (errors.length) {
|
||||
const errorMessage = `animation validation failed:\n${errors.join("\n")}`;
|
||||
const errorMessage = `animation validation failed:\n${errors.join('\n')}`;
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
this._animationAst = ast;
|
||||
@ -42,7 +42,7 @@ export class Animation {
|
||||
this._driver, element, this._animationAst, ENTER_CLASSNAME, LEAVE_CLASSNAME, start, dest,
|
||||
options, subInstructions, errors);
|
||||
if (errors.length) {
|
||||
const errorMessage = `animation building failed:\n${errors.join("\n")}`;
|
||||
const errorMessage = `animation building failed:\n${errors.join('\n')}`;
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
return result;
|
||||
|
@ -74,7 +74,9 @@ export interface StyleAst extends Ast<AnimationMetadataType.Style> {
|
||||
isEmptyStep?: boolean;
|
||||
}
|
||||
|
||||
export interface KeyframesAst extends Ast<AnimationMetadataType.Keyframes> { styles: StyleAst[]; }
|
||||
export interface KeyframesAst extends Ast<AnimationMetadataType.Keyframes> {
|
||||
styles: StyleAst[];
|
||||
}
|
||||
|
||||
export interface ReferenceAst extends Ast<AnimationMetadataType.Reference> {
|
||||
animation: Ast<AnimationMetadataType>;
|
||||
|
@ -5,11 +5,11 @@
|
||||
* 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, AnimateTimings, AnimationAnimateChildMetadata, AnimationAnimateMetadata, AnimationAnimateRefMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationMetadataType, AnimationOptions, AnimationQueryMetadata, AnimationQueryOptions, AnimationReferenceMetadata, AnimationSequenceMetadata, AnimationStaggerMetadata, AnimationStateMetadata, AnimationStyleMetadata, AnimationTransitionMetadata, AnimationTriggerMetadata, style, ɵStyleData} from '@angular/animations';
|
||||
import {AnimateTimings, AnimationAnimateChildMetadata, AnimationAnimateMetadata, AnimationAnimateRefMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationMetadataType, AnimationOptions, AnimationQueryMetadata, AnimationQueryOptions, AnimationReferenceMetadata, AnimationSequenceMetadata, AnimationStaggerMetadata, AnimationStateMetadata, AnimationStyleMetadata, AnimationTransitionMetadata, AnimationTriggerMetadata, AUTO_STYLE, style, ɵStyleData} from '@angular/animations';
|
||||
|
||||
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, visitDslNode} from '../util';
|
||||
import {copyObj, ENTER_SELECTOR, extractStyleParams, iteratorToArray, LEAVE_SELECTOR, NG_ANIMATING_SELECTOR, NG_TRIGGER_SELECTOR, normalizeAnimationEntry, resolveTiming, SUBSTITUTION_EXPR_START, 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} from './animation_dsl_visitor';
|
||||
@ -55,7 +55,7 @@ const SELF_TOKEN_REGEX = new RegExp(`\s*${SELF_TOKEN}\s*,?`, 'g');
|
||||
* Otherwise an error will be thrown.
|
||||
*/
|
||||
export function buildAnimationAst(
|
||||
driver: AnimationDriver, metadata: AnimationMetadata | AnimationMetadata[],
|
||||
driver: AnimationDriver, metadata: AnimationMetadata|AnimationMetadata[],
|
||||
errors: any[]): Ast<AnimationMetadataType> {
|
||||
return new AnimationAstBuilderVisitor(driver).build(metadata, errors);
|
||||
}
|
||||
@ -114,7 +114,11 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
|
||||
|
||||
return {
|
||||
type: AnimationMetadataType.Trigger,
|
||||
name: metadata.name, states, transitions, queryCount, depCount,
|
||||
name: metadata.name,
|
||||
states,
|
||||
transitions,
|
||||
queryCount,
|
||||
depCount,
|
||||
options: null
|
||||
};
|
||||
}
|
||||
@ -139,8 +143,10 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
|
||||
});
|
||||
if (missingSubs.size) {
|
||||
const missingSubsArr = iteratorToArray(missingSubs.values());
|
||||
context.errors.push(
|
||||
`state("${metadata.name}", ...) must define default values for all the following style substitutions: ${missingSubsArr.join(', ')}`);
|
||||
context.errors.push(`state("${
|
||||
metadata
|
||||
.name}", ...) must define default values for all the following style substitutions: ${
|
||||
missingSubsArr.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,7 +216,7 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
|
||||
let isEmpty = false;
|
||||
if (!styleMetadata) {
|
||||
isEmpty = true;
|
||||
const newStyleData: {[prop: string]: string | number} = {};
|
||||
const newStyleData: {[prop: string]: string|number} = {};
|
||||
if (timingAst.easing) {
|
||||
newStyleData['easing'] = timingAst.easing;
|
||||
}
|
||||
@ -239,9 +245,9 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
|
||||
|
||||
private _makeStyleAst(metadata: AnimationStyleMetadata, context: AnimationAstBuilderContext):
|
||||
StyleAst {
|
||||
const styles: (ɵStyleData | string)[] = [];
|
||||
const styles: (ɵStyleData|string)[] = [];
|
||||
if (Array.isArray(metadata.styles)) {
|
||||
(metadata.styles as(ɵStyleData | string)[]).forEach(styleTuple => {
|
||||
(metadata.styles as (ɵStyleData | string)[]).forEach(styleTuple => {
|
||||
if (typeof styleTuple == 'string') {
|
||||
if (styleTuple == AUTO_STYLE) {
|
||||
styles.push(styleTuple);
|
||||
@ -282,7 +288,8 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
|
||||
type: AnimationMetadataType.Style,
|
||||
styles,
|
||||
easing: collectedEasing,
|
||||
offset: metadata.offset, containsDynamicStyles,
|
||||
offset: metadata.offset,
|
||||
containsDynamicStyles,
|
||||
options: null
|
||||
};
|
||||
}
|
||||
@ -300,19 +307,22 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
|
||||
|
||||
Object.keys(tuple).forEach(prop => {
|
||||
if (!this._driver.validateStyleProperty(prop)) {
|
||||
context.errors.push(
|
||||
`The provided animation property "${prop}" is not a supported CSS property for animations`);
|
||||
context.errors.push(`The provided animation property "${
|
||||
prop}" is not a supported CSS property for animations`);
|
||||
return;
|
||||
}
|
||||
|
||||
const collectedStyles = context.collectedStyles[context.currentQuerySelector !];
|
||||
const collectedStyles = context.collectedStyles[context.currentQuerySelector!];
|
||||
const collectedEntry = collectedStyles[prop];
|
||||
let updateCollectedStyle = true;
|
||||
if (collectedEntry) {
|
||||
if (startTime != endTime && startTime >= collectedEntry.startTime &&
|
||||
endTime <= collectedEntry.endTime) {
|
||||
context.errors.push(
|
||||
`The CSS property "${prop}" that exists between the times of "${collectedEntry.startTime}ms" and "${collectedEntry.endTime}ms" is also being animated in a parallel animation between the times of "${startTime}ms" and "${endTime}ms"`);
|
||||
context.errors.push(`The CSS property "${prop}" that exists between the times of "${
|
||||
collectedEntry.startTime}ms" and "${
|
||||
collectedEntry
|
||||
.endTime}ms" is also being animated in a parallel animation between the times of "${
|
||||
startTime}ms" and "${endTime}ms"`);
|
||||
updateCollectedStyle = false;
|
||||
}
|
||||
|
||||
@ -383,7 +393,7 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
|
||||
|
||||
const limit = length - 1;
|
||||
const currentTime = context.currentTime;
|
||||
const currentAnimateTimings = context.currentAnimateTimings !;
|
||||
const currentAnimateTimings = context.currentAnimateTimings!;
|
||||
const animateDuration = currentAnimateTimings.duration;
|
||||
keyframes.forEach((kf, i) => {
|
||||
const offset = generatedOffset > 0 ? (i == limit ? 1 : (generatedOffset * i)) : offsets[i];
|
||||
@ -427,7 +437,7 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
|
||||
}
|
||||
|
||||
visitQuery(metadata: AnimationQueryMetadata, context: AnimationAstBuilderContext): QueryAst {
|
||||
const parentSelector = context.currentQuerySelector !;
|
||||
const parentSelector = context.currentQuerySelector!;
|
||||
const options = (metadata.options || {}) as AnimationQueryOptions;
|
||||
|
||||
context.queryCount++;
|
||||
@ -445,7 +455,9 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
|
||||
type: AnimationMetadataType.Query,
|
||||
selector,
|
||||
limit: options.limit || 0,
|
||||
optional: !!options.optional, includeSelf, animation,
|
||||
optional: !!options.optional,
|
||||
includeSelf,
|
||||
animation,
|
||||
originalSelector: metadata.selector,
|
||||
options: normalizeAnimationOptions(metadata.options)
|
||||
};
|
||||
@ -462,7 +474,8 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
|
||||
|
||||
return {
|
||||
type: AnimationMetadataType.Stagger,
|
||||
animation: visitDslNode(this, normalizeAnimationEntry(metadata.animation), context), timings,
|
||||
animation: visitDslNode(this, normalizeAnimationEntry(metadata.animation), context),
|
||||
timings,
|
||||
options: null
|
||||
};
|
||||
}
|
||||
@ -483,7 +496,7 @@ function normalizeSelector(selector: string): [string, boolean] {
|
||||
}
|
||||
|
||||
|
||||
function normalizeParams(obj: {[key: string]: any} | any): {[key: string]: any}|null {
|
||||
function normalizeParams(obj: {[key: string]: any}|any): {[key: string]: any}|null {
|
||||
return obj ? copyObj(obj) : null;
|
||||
}
|
||||
|
||||
@ -504,7 +517,7 @@ export class AnimationAstBuilderContext {
|
||||
constructor(public errors: any[]) {}
|
||||
}
|
||||
|
||||
function consumeOffset(styles: ɵStyleData | string | (ɵStyleData | string)[]): number|null {
|
||||
function consumeOffset(styles: ɵStyleData|string|(ɵStyleData | string)[]): number|null {
|
||||
if (typeof styles == 'string') return null;
|
||||
|
||||
let offset: number|null = null;
|
||||
@ -529,7 +542,7 @@ function isObject(value: any): boolean {
|
||||
return !Array.isArray(value) && typeof value == 'object';
|
||||
}
|
||||
|
||||
function constructTimingAst(value: string | number | AnimateTimings, errors: any[]) {
|
||||
function constructTimingAst(value: string|number|AnimateTimings, errors: any[]) {
|
||||
let timings: AnimateTimings|null = null;
|
||||
if (value.hasOwnProperty('duration')) {
|
||||
timings = value as AnimateTimings;
|
||||
@ -551,11 +564,11 @@ function constructTimingAst(value: string | number | AnimateTimings, errors: any
|
||||
return makeTimingAst(timings.duration, timings.delay, timings.easing);
|
||||
}
|
||||
|
||||
function normalizeAnimationOptions(options: AnimationOptions | null): AnimationOptions {
|
||||
function normalizeAnimationOptions(options: AnimationOptions|null): AnimationOptions {
|
||||
if (options) {
|
||||
options = copyObj(options);
|
||||
if (options['params']) {
|
||||
options['params'] = normalizeParams(options['params']) !;
|
||||
options['params'] = normalizeParams(options['params'])!;
|
||||
}
|
||||
} else {
|
||||
options = {};
|
||||
@ -563,6 +576,6 @@ function normalizeAnimationOptions(options: AnimationOptions | null): AnimationO
|
||||
return options;
|
||||
}
|
||||
|
||||
function makeTimingAst(duration: number, delay: number, easing: string | null): TimingAst {
|
||||
function makeTimingAst(duration: number, delay: number, easing: string|null): TimingAst {
|
||||
return {duration, delay, easing};
|
||||
}
|
||||
|
@ -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, AnimateChildOptions, AnimateTimings, AnimationMetadataType, AnimationOptions, AnimationQueryOptions, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations';
|
||||
import {AnimateChildOptions, AnimateTimings, AnimationMetadataType, AnimationOptions, AnimationQueryOptions, AUTO_STYLE, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations';
|
||||
|
||||
import {AnimationDriver} from '../render/animation_driver';
|
||||
import {copyObj, copyStyles, interpolateParams, iteratorToArray, resolveTiming, resolveTimingValue, visitDslNode} from '../util';
|
||||
@ -301,7 +301,7 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
|
||||
|
||||
visitStyle(ast: StyleAst, context: AnimationTimelineContext) {
|
||||
const timeline = context.currentTimeline;
|
||||
const timings = context.currentAnimateTimings !;
|
||||
const timings = context.currentAnimateTimings!;
|
||||
|
||||
// this is a special case for when a style() call
|
||||
// directly follows an animate() call (but not inside of an animate() call)
|
||||
@ -320,8 +320,8 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
|
||||
}
|
||||
|
||||
visitKeyframes(ast: KeyframesAst, context: AnimationTimelineContext) {
|
||||
const currentAnimateTimings = context.currentAnimateTimings !;
|
||||
const startTime = (context.currentTimeline !).duration;
|
||||
const currentAnimateTimings = context.currentAnimateTimings!;
|
||||
const startTime = (context.currentTimeline!).duration;
|
||||
const duration = currentAnimateTimings.duration;
|
||||
const innerContext = context.createSubContext();
|
||||
const innerTimeline = innerContext.currentTimeline;
|
||||
@ -351,8 +351,9 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
|
||||
const options = (ast.options || {}) as AnimationQueryOptions;
|
||||
const delay = options.delay ? resolveTimingValue(options.delay) : 0;
|
||||
|
||||
if (delay && (context.previousNode.type === AnimationMetadataType.Style ||
|
||||
(startTime == 0 && context.currentTimeline.getCurrentStyleProperties().length))) {
|
||||
if (delay &&
|
||||
(context.previousNode.type === AnimationMetadataType.Style ||
|
||||
(startTime == 0 && context.currentTimeline.getCurrentStyleProperties().length))) {
|
||||
context.currentTimeline.snapshotCurrentStyles();
|
||||
context.previousNode = DEFAULT_NOOP_PREVIOUS_NODE;
|
||||
}
|
||||
@ -365,7 +366,6 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
|
||||
context.currentQueryTotal = elms.length;
|
||||
let sameElementTimeline: TimelineBuilder|null = null;
|
||||
elms.forEach((element, i) => {
|
||||
|
||||
context.currentQueryIndex = i;
|
||||
const innerContext = context.createSubContext(ast.options, element);
|
||||
if (delay) {
|
||||
@ -400,7 +400,7 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
|
||||
}
|
||||
|
||||
visitStagger(ast: StaggerAst, context: AnimationTimelineContext) {
|
||||
const parentContext = context.parentContext !;
|
||||
const parentContext = context.parentContext!;
|
||||
const tl = context.currentTimeline;
|
||||
const timings = ast.timings;
|
||||
const duration = Math.abs(timings.duration);
|
||||
@ -460,7 +460,9 @@ export class AnimationTimelineContext {
|
||||
timelines.push(this.currentTimeline);
|
||||
}
|
||||
|
||||
get params() { return this.options.params; }
|
||||
get params() {
|
||||
return this.options.params;
|
||||
}
|
||||
|
||||
updateOptions(options: AnimationOptions|null, skipIfExists?: boolean) {
|
||||
if (!options) return;
|
||||
@ -479,7 +481,7 @@ export class AnimationTimelineContext {
|
||||
|
||||
const newParams = newOptions.params;
|
||||
if (newParams) {
|
||||
let paramsToUpdate: {[name: string]: any} = optionsToUpdate.params !;
|
||||
let paramsToUpdate: {[name: string]: any} = optionsToUpdate.params!;
|
||||
if (!paramsToUpdate) {
|
||||
paramsToUpdate = this.options.params = {};
|
||||
}
|
||||
@ -498,7 +500,9 @@ export class AnimationTimelineContext {
|
||||
const oldParams = this.options.params;
|
||||
if (oldParams) {
|
||||
const params: {[name: string]: any} = options['params'] = {};
|
||||
Object.keys(oldParams).forEach(name => { params[name] = oldParams[name]; });
|
||||
Object.keys(oldParams).forEach(name => {
|
||||
params[name] = oldParams[name];
|
||||
});
|
||||
}
|
||||
}
|
||||
return options;
|
||||
@ -576,8 +580,8 @@ export class AnimationTimelineContext {
|
||||
}
|
||||
|
||||
if (!optional && results.length == 0) {
|
||||
errors.push(
|
||||
`\`query("${originalSelector}")\` returned zero elements. (Use \`query("${originalSelector}", { optional: true })\` if you wish to allow this.)`);
|
||||
errors.push(`\`query("${originalSelector}")\` returned zero elements. (Use \`query("${
|
||||
originalSelector}", { optional: true })\` if you wish to allow this.)`);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@ -587,7 +591,7 @@ export class AnimationTimelineContext {
|
||||
export class TimelineBuilder {
|
||||
public duration: number = 0;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public easing !: string | null;
|
||||
public easing!: string|null;
|
||||
private _previousKeyframe: ɵStyleData = {};
|
||||
private _currentKeyframe: ɵStyleData = {};
|
||||
private _keyframes = new Map<number, ɵStyleData>();
|
||||
@ -606,7 +610,7 @@ export class TimelineBuilder {
|
||||
}
|
||||
|
||||
this._localTimelineStyles = Object.create(this._backFill, {});
|
||||
this._globalTimelineStyles = this._elementTimelineStylesLookup.get(element) !;
|
||||
this._globalTimelineStyles = this._elementTimelineStylesLookup.get(element)!;
|
||||
if (!this._globalTimelineStyles) {
|
||||
this._globalTimelineStyles = this._localTimelineStyles;
|
||||
this._elementTimelineStylesLookup.set(element, this._localTimelineStyles);
|
||||
@ -625,9 +629,13 @@ export class TimelineBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
getCurrentStyleProperties(): string[] { return Object.keys(this._currentKeyframe); }
|
||||
getCurrentStyleProperties(): string[] {
|
||||
return Object.keys(this._currentKeyframe);
|
||||
}
|
||||
|
||||
get currentTime() { return this.startTime + this.duration; }
|
||||
get currentTime() {
|
||||
return this.startTime + this.duration;
|
||||
}
|
||||
|
||||
delayNextStep(delay: number) {
|
||||
// in the event that a style() step is placed right before a stagger()
|
||||
@ -656,7 +664,7 @@ export class TimelineBuilder {
|
||||
if (this._currentKeyframe) {
|
||||
this._previousKeyframe = this._currentKeyframe;
|
||||
}
|
||||
this._currentKeyframe = this._keyframes.get(this.duration) !;
|
||||
this._currentKeyframe = this._keyframes.get(this.duration)!;
|
||||
if (!this._currentKeyframe) {
|
||||
this._currentKeyframe = Object.create(this._backFill, {});
|
||||
this._keyframes.set(this.duration, this._currentKeyframe);
|
||||
@ -680,7 +688,9 @@ export class TimelineBuilder {
|
||||
this._styleSummary[prop] = {time: this.currentTime, value};
|
||||
}
|
||||
|
||||
allowOnlyTimelineStyles() { return this._currentEmptyStepKeyframe !== this._currentKeyframe; }
|
||||
allowOnlyTimelineStyles() {
|
||||
return this._currentEmptyStepKeyframe !== this._currentKeyframe;
|
||||
}
|
||||
|
||||
applyEmptyStep(easing: string|null) {
|
||||
if (easing) {
|
||||
@ -748,7 +758,9 @@ export class TimelineBuilder {
|
||||
});
|
||||
}
|
||||
|
||||
getFinalKeyframe() { return this._keyframes.get(this.duration); }
|
||||
getFinalKeyframe() {
|
||||
return this._keyframes.get(this.duration);
|
||||
}
|
||||
|
||||
get properties() {
|
||||
const properties: string[] = [];
|
||||
@ -820,7 +832,9 @@ class SubTimelineBuilder extends TimelineBuilder {
|
||||
this.timings = {duration: timings.duration, delay: timings.delay, easing: timings.easing};
|
||||
}
|
||||
|
||||
containsAnimation(): boolean { return this.keyframes.length > 1; }
|
||||
containsAnimation(): boolean {
|
||||
return this.keyframes.length > 1;
|
||||
}
|
||||
|
||||
buildKeyframes(): AnimationTimelineInstruction {
|
||||
let keyframes = this.keyframes;
|
||||
@ -883,13 +897,15 @@ function roundOffset(offset: number, decimalPoints = 3): number {
|
||||
return Math.round(offset * mult) / mult;
|
||||
}
|
||||
|
||||
function flattenStyles(input: (ɵStyleData | string)[], allStyles: ɵStyleData) {
|
||||
function flattenStyles(input: (ɵStyleData|string)[], allStyles: ɵStyleData) {
|
||||
const styles: ɵStyleData = {};
|
||||
let allProperties: string[];
|
||||
input.forEach(token => {
|
||||
if (token === '*') {
|
||||
allProperties = allProperties || Object.keys(allStyles);
|
||||
allProperties.forEach(prop => { styles[prop] = AUTO_STYLE; });
|
||||
allProperties.forEach(prop => {
|
||||
styles[prop] = AUTO_STYLE;
|
||||
});
|
||||
} else {
|
||||
copyStyles(token as ɵStyleData, false, styles);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ export interface AnimationTimelineInstruction extends AnimationEngineInstruction
|
||||
|
||||
export function createTimelineInstruction(
|
||||
element: any, keyframes: ɵStyleData[], preStyleProps: string[], postStyleProps: string[],
|
||||
duration: number, delay: number, easing: string | null = null,
|
||||
duration: number, delay: number, easing: string|null = null,
|
||||
subTimeline: boolean = false): AnimationTimelineInstruction {
|
||||
return {
|
||||
type: AnimationTransitionInstructionType.TimelineAnimation,
|
||||
@ -33,6 +33,8 @@ export function createTimelineInstruction(
|
||||
postStyleProps,
|
||||
duration,
|
||||
delay,
|
||||
totalTime: duration + delay, easing, subTimeline
|
||||
totalTime: duration + delay,
|
||||
easing,
|
||||
subTimeline
|
||||
};
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export declare type TransitionMatcherFn =
|
||||
(fromState: any, toState: any, element: any, params: {[key: string]: any}) => boolean;
|
||||
|
||||
export function parseTransitionExpr(
|
||||
transitionValue: string | TransitionMatcherFn, errors: string[]): TransitionMatcherFn[] {
|
||||
transitionValue: string|TransitionMatcherFn, errors: string[]): TransitionMatcherFn[] {
|
||||
const expressions: TransitionMatcherFn[] = [];
|
||||
if (typeof transitionValue == 'string') {
|
||||
transitionValue.split(/\s*,\s*/).forEach(
|
||||
|
@ -55,13 +55,16 @@ export class AnimationTransitionFactory {
|
||||
|
||||
const animationOptions = {params: {...transitionAnimationParams, ...nextAnimationParams}};
|
||||
|
||||
const timelines = skipAstBuild ? [] : buildAnimationTimelines(
|
||||
driver, element, this.ast.animation, enterClassName,
|
||||
leaveClassName, currentStateStyles, nextStateStyles,
|
||||
animationOptions, subInstructions, errors);
|
||||
const timelines = skipAstBuild ?
|
||||
[] :
|
||||
buildAnimationTimelines(
|
||||
driver, element, this.ast.animation, enterClassName, leaveClassName, currentStateStyles,
|
||||
nextStateStyles, animationOptions, subInstructions, errors);
|
||||
|
||||
let totalTime = 0;
|
||||
timelines.forEach(tl => { totalTime = Math.max(tl.duration + tl.delay, totalTime); });
|
||||
timelines.forEach(tl => {
|
||||
totalTime = Math.max(tl.duration + tl.delay, totalTime);
|
||||
});
|
||||
|
||||
if (errors.length) {
|
||||
return createTransitionInstruction(
|
||||
|
@ -22,8 +22,8 @@ export function buildTrigger(name: string, ast: TriggerAst): AnimationTrigger {
|
||||
}
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
* @publicApi
|
||||
*/
|
||||
export class AnimationTrigger {
|
||||
public transitionFactories: AnimationTransitionFactory[] = [];
|
||||
public fallbackTransition: AnimationTransitionFactory;
|
||||
@ -45,7 +45,9 @@ export class AnimationTrigger {
|
||||
this.fallbackTransition = createFallbackTransition(name, this.states);
|
||||
}
|
||||
|
||||
get containsQueries() { return this.ast.queryCount > 0; }
|
||||
get containsQueries() {
|
||||
return this.ast.queryCount > 0;
|
||||
}
|
||||
|
||||
matchTransition(currentState: any, nextState: any, element: any, params: {[key: string]: any}):
|
||||
AnimationTransitionFactory|null {
|
||||
|
@ -28,7 +28,11 @@ export class ElementInstructionMap {
|
||||
existingInstructions.push(...instructions);
|
||||
}
|
||||
|
||||
has(element: any): boolean { return this._map.has(element); }
|
||||
has(element: any): boolean {
|
||||
return this._map.has(element);
|
||||
}
|
||||
|
||||
clear() { this._map.clear(); }
|
||||
clear() {
|
||||
this._map.clear();
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,9 @@ export abstract class AnimationStyleNormalizer {
|
||||
* @publicApi
|
||||
*/
|
||||
export class NoopAnimationStyleNormalizer {
|
||||
normalizePropertyName(propertyName: string, errors: string[]): string { return propertyName; }
|
||||
normalizePropertyName(propertyName: string, errors: string[]): string {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
normalizeStyleValue(
|
||||
userProvidedProperty: string, normalizedProperty: string, value: string|number,
|
||||
|
@ -13,6 +13,6 @@ export {AnimationEngine as ɵAnimationEngine} from './render/animation_engine_ne
|
||||
export {CssKeyframesDriver as ɵCssKeyframesDriver} from './render/css_keyframes/css_keyframes_driver';
|
||||
export {CssKeyframesPlayer as ɵCssKeyframesPlayer} from './render/css_keyframes/css_keyframes_player';
|
||||
export {containsElement as ɵcontainsElement, invokeQuery as ɵinvokeQuery, matchesElement as ɵmatchesElement, validateStyleProperty as ɵvalidateStyleProperty} from './render/shared';
|
||||
export {WebAnimationsDriver as ɵWebAnimationsDriver, supportsWebAnimations as ɵsupportsWebAnimations} from './render/web_animations/web_animations_driver';
|
||||
export {supportsWebAnimations as ɵsupportsWebAnimations, WebAnimationsDriver as ɵWebAnimationsDriver} from './render/web_animations/web_animations_driver';
|
||||
export {WebAnimationsPlayer as ɵWebAnimationsPlayer} from './render/web_animations/web_animations_player';
|
||||
export {allowPreviousPlayerStylesMerge as ɵallowPreviousPlayerStylesMerge} from './util';
|
||||
|
@ -15,13 +15,17 @@ import {containsElement, invokeQuery, matchesElement, validateStyleProperty} fro
|
||||
*/
|
||||
@Injectable()
|
||||
export class NoopAnimationDriver implements AnimationDriver {
|
||||
validateStyleProperty(prop: string): boolean { return validateStyleProperty(prop); }
|
||||
validateStyleProperty(prop: string): boolean {
|
||||
return validateStyleProperty(prop);
|
||||
}
|
||||
|
||||
matchesElement(element: any, selector: string): boolean {
|
||||
return matchesElement(element, selector);
|
||||
}
|
||||
|
||||
containsElement(elm1: any, elm2: any): boolean { return containsElement(elm1, elm2); }
|
||||
containsElement(elm1: any, elm2: any): boolean {
|
||||
return containsElement(elm1, elm2);
|
||||
}
|
||||
|
||||
query(element: any, selector: string, multi: boolean): any[] {
|
||||
return invokeQuery(element, selector, multi);
|
||||
@ -32,7 +36,7 @@ export class NoopAnimationDriver implements AnimationDriver {
|
||||
}
|
||||
|
||||
animate(
|
||||
element: any, keyframes: {[key: string]: string | number}[], duration: number, delay: number,
|
||||
element: any, keyframes: {[key: string]: string|number}[], duration: number, delay: number,
|
||||
easing: string, previousPlayers: any[] = [],
|
||||
scrubberAccessRequested?: boolean): AnimationPlayer {
|
||||
return new NoopAnimationPlayer(duration, delay);
|
||||
@ -56,6 +60,6 @@ export abstract class AnimationDriver {
|
||||
abstract computeStyle(element: any, prop: string, defaultValue?: string): string;
|
||||
|
||||
abstract animate(
|
||||
element: any, keyframes: {[key: string]: string | number}[], duration: number, delay: number,
|
||||
element: any, keyframes: {[key: string]: string|number}[], duration: number, delay: number,
|
||||
easing?: string|null, previousPlayers?: any[], scrubberAccessRequested?: boolean): any;
|
||||
}
|
||||
|
@ -5,6 +5,11 @@
|
||||
* 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
|
||||
*/
|
||||
export const enum AnimationTransitionInstructionType {TransitionAnimation, TimelineAnimation}
|
||||
export const enum AnimationTransitionInstructionType {
|
||||
TransitionAnimation,
|
||||
TimelineAnimation
|
||||
}
|
||||
|
||||
export interface AnimationEngineInstruction { type: AnimationTransitionInstructionType; }
|
||||
export interface AnimationEngineInstruction {
|
||||
type: AnimationTransitionInstructionType;
|
||||
}
|
||||
|
@ -45,8 +45,8 @@ export class AnimationEngine {
|
||||
const ast =
|
||||
buildAnimationAst(this._driver, metadata as AnimationMetadata, errors) as TriggerAst;
|
||||
if (errors.length) {
|
||||
throw new Error(
|
||||
`The animation trigger "${name}" has failed to build due to the following errors:\n - ${errors.join("\n - ")}`);
|
||||
throw new Error(`The animation trigger "${
|
||||
name}" has failed to build due to the following errors:\n - ${errors.join('\n - ')}`);
|
||||
}
|
||||
trigger = buildTrigger(name, ast);
|
||||
this._triggerCache[cacheKey] = trigger;
|
||||
@ -95,12 +95,16 @@ export class AnimationEngine {
|
||||
return this._transitionEngine.listen(namespaceId, element, eventName, eventPhase, callback);
|
||||
}
|
||||
|
||||
flush(microtaskId: number = -1): void { this._transitionEngine.flush(microtaskId); }
|
||||
flush(microtaskId: number = -1): void {
|
||||
this._transitionEngine.flush(microtaskId);
|
||||
}
|
||||
|
||||
get players(): AnimationPlayer[] {
|
||||
return (this._transitionEngine.players as AnimationPlayer[])
|
||||
.concat(this._timelineEngine.players as AnimationPlayer[]);
|
||||
}
|
||||
|
||||
whenRenderingDone(): Promise<any> { return this._transitionEngine.whenRenderingDone(); }
|
||||
whenRenderingDone(): Promise<any> {
|
||||
return this._transitionEngine.whenRenderingDone();
|
||||
}
|
||||
}
|
||||
|
@ -23,13 +23,17 @@ export class CssKeyframesDriver implements AnimationDriver {
|
||||
private readonly _head: any = document.querySelector('head');
|
||||
private _warningIssued = false;
|
||||
|
||||
validateStyleProperty(prop: string): boolean { return validateStyleProperty(prop); }
|
||||
validateStyleProperty(prop: string): boolean {
|
||||
return validateStyleProperty(prop);
|
||||
}
|
||||
|
||||
matchesElement(element: any, selector: string): boolean {
|
||||
return matchesElement(element, selector);
|
||||
}
|
||||
|
||||
containsElement(elm1: any, elm2: any): boolean { return containsElement(elm1, elm2); }
|
||||
containsElement(elm1: any, elm2: any): boolean {
|
||||
return containsElement(elm1, elm2);
|
||||
}
|
||||
|
||||
query(element: any, selector: string, multi: boolean): any[] {
|
||||
return invokeQuery(element, selector, multi);
|
||||
@ -104,7 +108,7 @@ export class CssKeyframesDriver implements AnimationDriver {
|
||||
|
||||
const animationName = `${KEYFRAMES_NAME_PREFIX}${this._count++}`;
|
||||
const kfElm = this.buildKeyframeElement(element, animationName, keyframes);
|
||||
document.querySelector('head') !.appendChild(kfElm);
|
||||
document.querySelector('head')!.appendChild(kfElm);
|
||||
|
||||
const specialStyles = packageNonAnimatableStyles(element, keyframes);
|
||||
const player = new CssKeyframesPlayer(
|
||||
@ -124,8 +128,8 @@ export class CssKeyframesDriver implements AnimationDriver {
|
||||
}
|
||||
}
|
||||
|
||||
function flattenKeyframesIntoStyles(
|
||||
keyframes: null | {[key: string]: any} | {[key: string]: any}[]): {[key: string]: any} {
|
||||
function flattenKeyframesIntoStyles(keyframes: null|{[key: string]: any}|
|
||||
{[key: string]: any}[]): {[key: string]: any} {
|
||||
let flatKeyframes: {[key: string]: any} = {};
|
||||
if (keyframes) {
|
||||
const kfs = Array.isArray(keyframes) ? keyframes : [keyframes];
|
||||
|
@ -14,7 +14,12 @@ import {ElementAnimationStyleHandler} from './element_animation_style_handler';
|
||||
const DEFAULT_FILL_MODE = 'forwards';
|
||||
const DEFAULT_EASING = 'linear';
|
||||
|
||||
export const enum AnimatorControlState {INITIALIZED = 1, STARTED = 2, FINISHED = 3, DESTROYED = 4}
|
||||
export const enum AnimatorControlState {
|
||||
INITIALIZED = 1,
|
||||
STARTED = 2,
|
||||
FINISHED = 3,
|
||||
DESTROYED = 4
|
||||
}
|
||||
|
||||
export class CssKeyframesPlayer implements AnimationPlayer {
|
||||
private _onDoneFns: Function[] = [];
|
||||
@ -23,10 +28,10 @@ export class CssKeyframesPlayer implements AnimationPlayer {
|
||||
|
||||
private _started = false;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
private _styler !: ElementAnimationStyleHandler;
|
||||
private _styler!: ElementAnimationStyleHandler;
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public parentPlayer !: AnimationPlayer;
|
||||
public parentPlayer!: AnimationPlayer;
|
||||
public readonly totalTime: number;
|
||||
public readonly easing: string;
|
||||
public currentSnapshot: {[key: string]: string} = {};
|
||||
@ -34,7 +39,7 @@ export class CssKeyframesPlayer implements AnimationPlayer {
|
||||
private _state: AnimatorControlState = 0;
|
||||
|
||||
constructor(
|
||||
public readonly element: any, public readonly keyframes: {[key: string]: string | number}[],
|
||||
public readonly element: any, public readonly keyframes: {[key: string]: string|number}[],
|
||||
public readonly animationName: string, private readonly _duration: number,
|
||||
private readonly _delay: number, easing: string,
|
||||
private readonly _finalStyles: {[key: string]: any},
|
||||
@ -44,11 +49,17 @@ export class CssKeyframesPlayer implements AnimationPlayer {
|
||||
this._buildStyler();
|
||||
}
|
||||
|
||||
onStart(fn: () => void): void { this._onStartFns.push(fn); }
|
||||
onStart(fn: () => void): void {
|
||||
this._onStartFns.push(fn);
|
||||
}
|
||||
|
||||
onDone(fn: () => void): void { this._onDoneFns.push(fn); }
|
||||
onDone(fn: () => void): void {
|
||||
this._onDoneFns.push(fn);
|
||||
}
|
||||
|
||||
onDestroy(fn: () => void): void { this._onDestroyFns.push(fn); }
|
||||
onDestroy(fn: () => void): void {
|
||||
this._onDestroyFns.push(fn);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.init();
|
||||
@ -86,11 +97,17 @@ export class CssKeyframesPlayer implements AnimationPlayer {
|
||||
this._flushDoneFns();
|
||||
}
|
||||
|
||||
setPosition(value: number) { this._styler.setPosition(value); }
|
||||
setPosition(value: number) {
|
||||
this._styler.setPosition(value);
|
||||
}
|
||||
|
||||
getPosition(): number { return this._styler.getPosition(); }
|
||||
getPosition(): number {
|
||||
return this._styler.getPosition();
|
||||
}
|
||||
|
||||
hasStarted(): boolean { return this._state >= AnimatorControlState.STARTED; }
|
||||
hasStarted(): boolean {
|
||||
return this._state >= AnimatorControlState.STARTED;
|
||||
}
|
||||
init(): void {
|
||||
if (this._state >= AnimatorControlState.INITIALIZED) return;
|
||||
this._state = AnimatorControlState.INITIALIZED;
|
||||
|
@ -22,7 +22,7 @@ export class DirectStylePlayer extends NoopAnimationPlayer {
|
||||
if (this.__initialized || !this._startingStyles) return;
|
||||
this.__initialized = true;
|
||||
Object.keys(this._styles).forEach(prop => {
|
||||
this._startingStyles ![prop] = this.element.style[prop];
|
||||
this._startingStyles![prop] = this.element.style[prop];
|
||||
});
|
||||
super.init();
|
||||
}
|
||||
@ -38,7 +38,7 @@ export class DirectStylePlayer extends NoopAnimationPlayer {
|
||||
destroy() {
|
||||
if (!this._startingStyles) return;
|
||||
Object.keys(this._startingStyles).forEach(prop => {
|
||||
const value = this._startingStyles ![prop];
|
||||
const value = this._startingStyles![prop];
|
||||
if (value) {
|
||||
this.element.style.setProperty(prop, value);
|
||||
} else {
|
||||
|
@ -28,14 +28,19 @@ export class ElementAnimationStyleHandler {
|
||||
apply() {
|
||||
applyKeyframeAnimation(
|
||||
this._element,
|
||||
`${this._duration}ms ${this._easing} ${this._delay}ms 1 normal ${this._fillMode} ${this._name}`);
|
||||
`${this._duration}ms ${this._easing} ${this._delay}ms 1 normal ${this._fillMode} ${
|
||||
this._name}`);
|
||||
addRemoveAnimationEvent(this._element, this._eventFn, false);
|
||||
this._startTime = Date.now();
|
||||
}
|
||||
|
||||
pause() { playPauseAnimation(this._element, this._name, 'paused'); }
|
||||
pause() {
|
||||
playPauseAnimation(this._element, this._name, 'paused');
|
||||
}
|
||||
|
||||
resume() { playPauseAnimation(this._element, this._name, 'running'); }
|
||||
resume() {
|
||||
playPauseAnimation(this._element, this._name, 'running');
|
||||
}
|
||||
|
||||
setPosition(position: number) {
|
||||
const index = findIndexForAnimation(this._element, this._name);
|
||||
@ -43,7 +48,9 @@ export class ElementAnimationStyleHandler {
|
||||
setAnimationStyle(this._element, 'Delay', `-${this._position}ms`, index);
|
||||
}
|
||||
|
||||
getPosition() { return this._position; }
|
||||
getPosition() {
|
||||
return this._position;
|
||||
}
|
||||
|
||||
private _handleCallback(event: any) {
|
||||
const timestamp = event._ngTestManualTimestamp || Date.now();
|
||||
@ -70,7 +77,7 @@ export class ElementAnimationStyleHandler {
|
||||
}
|
||||
}
|
||||
|
||||
function playPauseAnimation(element: any, name: string, status: 'running' | 'paused') {
|
||||
function playPauseAnimation(element: any, name: string, status: 'running'|'paused') {
|
||||
const index = findIndexForAnimation(element, name);
|
||||
setAnimationStyle(element, 'PlayState', status, index);
|
||||
}
|
||||
|
@ -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, AnimationEvent, AnimationPlayer, NoopAnimationPlayer, ɵAnimationGroupPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations';
|
||||
import {AnimationEvent, AnimationPlayer, AUTO_STYLE, NoopAnimationPlayer, ɵAnimationGroupPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations';
|
||||
|
||||
import {AnimationStyleNormalizer} from '../../src/dsl/style_normalization/animation_style_normalizer';
|
||||
import {AnimationDriver} from '../../src/render/animation_driver';
|
||||
@ -89,7 +89,7 @@ export function normalizeKeyframes(
|
||||
}
|
||||
|
||||
export function listenOnPlayer(
|
||||
player: AnimationPlayer, eventName: string, event: AnimationEvent | undefined,
|
||||
player: AnimationPlayer, eventName: string, event: AnimationEvent|undefined,
|
||||
callback: (event: any) => any) {
|
||||
switch (eventName) {
|
||||
case 'start':
|
||||
@ -125,7 +125,7 @@ export function makeAnimationEvent(
|
||||
}
|
||||
|
||||
export function getOrSetAsInMap(
|
||||
map: Map<any, any>| {[key: string]: any}, key: any, defaultValue: any) {
|
||||
map: Map<any, any>|{[key: string]: any}, key: any, defaultValue: any) {
|
||||
let value: any;
|
||||
if (map instanceof Map) {
|
||||
value = map.get(key);
|
||||
@ -161,7 +161,9 @@ let _query: (element: any, selector: string, multi: boolean) => any[] =
|
||||
const _isNode = isNode();
|
||||
if (_isNode || typeof Element !== 'undefined') {
|
||||
// this is well supported in all browsers
|
||||
_contains = (elm1: any, elm2: any) => { return elm1.contains(elm2) as boolean; };
|
||||
_contains = (elm1: any, elm2: any) => {
|
||||
return elm1.contains(elm2) as boolean;
|
||||
};
|
||||
|
||||
_matches = (() => {
|
||||
if (_isNode || Element.prototype.matches) {
|
||||
@ -203,15 +205,15 @@ let _IS_WEBKIT = false;
|
||||
export function validateStyleProperty(prop: string): boolean {
|
||||
if (!_CACHED_BODY) {
|
||||
_CACHED_BODY = getBodyNode() || {};
|
||||
_IS_WEBKIT = _CACHED_BODY !.style ? ('WebkitAppearance' in _CACHED_BODY !.style) : false;
|
||||
_IS_WEBKIT = _CACHED_BODY!.style ? ('WebkitAppearance' in _CACHED_BODY!.style) : false;
|
||||
}
|
||||
|
||||
let result = true;
|
||||
if (_CACHED_BODY !.style && !containsVendorPrefix(prop)) {
|
||||
result = prop in _CACHED_BODY !.style;
|
||||
if (_CACHED_BODY!.style && !containsVendorPrefix(prop)) {
|
||||
result = prop in _CACHED_BODY!.style;
|
||||
if (!result && _IS_WEBKIT) {
|
||||
const camelProp = 'Webkit' + prop.charAt(0).toUpperCase() + prop.substr(1);
|
||||
result = camelProp in _CACHED_BODY !.style;
|
||||
result = camelProp in _CACHED_BODY!.style;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ import {eraseStyles, setStyles} from '../util';
|
||||
* @returns an instance of `SpecialCasedStyles` if any special styles are detected otherwise `null`
|
||||
*/
|
||||
export function packageNonAnimatableStyles(
|
||||
element: any, styles: {[key: string]: any} | {[key: string]: any}[]): SpecialCasedStyles|null {
|
||||
element: any, styles: {[key: string]: any}|{[key: string]: any}[]): SpecialCasedStyles|null {
|
||||
let startStyles: {[key: string]: any}|null = null;
|
||||
let endStyles: {[key: string]: any}|null = null;
|
||||
if (Array.isArray(styles) && styles.length) {
|
||||
@ -47,7 +47,7 @@ export class SpecialCasedStyles {
|
||||
static initialStylesByElement = new WeakMap<any, {[key: string]: any}>();
|
||||
|
||||
private _state = SpecialCasedStylesState.Pending;
|
||||
private _initialStyles !: {[key: string]: any};
|
||||
private _initialStyles!: {[key: string]: any};
|
||||
|
||||
constructor(
|
||||
private _element: any, private _startStyles: {[key: string]: any}|null,
|
||||
|
@ -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, AnimationMetadataType, AnimationOptions, AnimationPlayer, ɵStyleData} from '@angular/animations';
|
||||
import {AnimationMetadata, AnimationMetadataType, AnimationOptions, AnimationPlayer, AUTO_STYLE, ɵStyleData} from '@angular/animations';
|
||||
|
||||
import {Ast} from '../dsl/animation_ast';
|
||||
import {buildAnimationAst} from '../dsl/animation_ast_builder';
|
||||
@ -34,7 +34,7 @@ export class TimelineAnimationEngine {
|
||||
const ast = buildAnimationAst(this._driver, metadata, errors);
|
||||
if (errors.length) {
|
||||
throw new Error(
|
||||
`Unable to build the animation due to the following errors: ${errors.join("\n")}`);
|
||||
`Unable to build the animation due to the following errors: ${errors.join('\n')}`);
|
||||
} else {
|
||||
this._animations[id] = ast;
|
||||
}
|
||||
@ -71,12 +71,13 @@ export class TimelineAnimationEngine {
|
||||
|
||||
if (errors.length) {
|
||||
throw new Error(
|
||||
`Unable to create the animation due to the following errors: ${errors.join("\n")}`);
|
||||
`Unable to create the animation due to the following errors: ${errors.join('\n')}`);
|
||||
}
|
||||
|
||||
autoStylesMap.forEach((styles, element) => {
|
||||
Object.keys(styles).forEach(
|
||||
prop => { styles[prop] = this._driver.computeStyle(element, prop, AUTO_STYLE); });
|
||||
Object.keys(styles).forEach(prop => {
|
||||
styles[prop] = this._driver.computeStyle(element, prop, AUTO_STYLE);
|
||||
});
|
||||
});
|
||||
|
||||
const players = instructions.map(i => {
|
||||
|
@ -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, AnimationOptions, AnimationPlayer, NoopAnimationPlayer, ɵAnimationGroupPlayer as AnimationGroupPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations';
|
||||
import {AnimationOptions, AnimationPlayer, AUTO_STYLE, NoopAnimationPlayer, ɵAnimationGroupPlayer as AnimationGroupPlayer, ɵPRE_STYLE as PRE_STYLE, ɵStyleData} from '@angular/animations';
|
||||
|
||||
import {AnimationTimelineInstruction} from '../dsl/animation_timeline_instruction';
|
||||
import {AnimationTransitionFactory} from '../dsl/animation_transition_factory';
|
||||
@ -13,7 +13,7 @@ import {AnimationTransitionInstruction} from '../dsl/animation_transition_instru
|
||||
import {AnimationTrigger} from '../dsl/animation_trigger';
|
||||
import {ElementInstructionMap} from '../dsl/element_instruction_map';
|
||||
import {AnimationStyleNormalizer} from '../dsl/style_normalization/animation_style_normalizer';
|
||||
import {ENTER_CLASSNAME, LEAVE_CLASSNAME, NG_ANIMATING_CLASSNAME, NG_ANIMATING_SELECTOR, NG_TRIGGER_CLASSNAME, NG_TRIGGER_SELECTOR, copyObj, eraseStyles, iteratorToArray, setStyles} from '../util';
|
||||
import {copyObj, ENTER_CLASSNAME, eraseStyles, iteratorToArray, LEAVE_CLASSNAME, NG_ANIMATING_CLASSNAME, NG_ANIMATING_SELECTOR, NG_TRIGGER_CLASSNAME, NG_TRIGGER_SELECTOR, setStyles} from '../util';
|
||||
|
||||
import {AnimationDriver} from './animation_driver';
|
||||
import {getOrSetAsInMap, listenOnPlayer, makeAnimationEvent, normalizeKeyframes, optimizeGroupPlayer} from './shared';
|
||||
@ -71,7 +71,9 @@ export class StateValue {
|
||||
public value: string;
|
||||
public options: AnimationOptions;
|
||||
|
||||
get params(): {[key: string]: any} { return this.options.params as{[key: string]: any}; }
|
||||
get params(): {[key: string]: any} {
|
||||
return this.options.params as {[key: string]: any};
|
||||
}
|
||||
|
||||
constructor(input: any, public namespaceId: string = '') {
|
||||
const isObj = input && input.hasOwnProperty('value');
|
||||
@ -92,7 +94,7 @@ export class StateValue {
|
||||
absorbOptions(options: AnimationOptions) {
|
||||
const newParams = options.params;
|
||||
if (newParams) {
|
||||
const oldParams = this.options.params !;
|
||||
const oldParams = this.options.params!;
|
||||
Object.keys(newParams).forEach(prop => {
|
||||
if (oldParams[prop] == null) {
|
||||
oldParams[prop] = newParams[prop];
|
||||
@ -263,7 +265,9 @@ export class AnimationTransitionNamespace {
|
||||
|
||||
if (!isFallbackTransition) {
|
||||
addClass(element, QUEUED_CLASSNAME);
|
||||
player.onStart(() => { removeClass(element, QUEUED_CLASSNAME); });
|
||||
player.onStart(() => {
|
||||
removeClass(element, QUEUED_CLASSNAME);
|
||||
});
|
||||
}
|
||||
|
||||
player.onDone(() => {
|
||||
@ -290,11 +294,14 @@ export class AnimationTransitionNamespace {
|
||||
deregister(name: string) {
|
||||
delete this._triggers[name];
|
||||
|
||||
this._engine.statesByElement.forEach((stateMap, element) => { delete stateMap[name]; });
|
||||
this._engine.statesByElement.forEach((stateMap, element) => {
|
||||
delete stateMap[name];
|
||||
});
|
||||
|
||||
this._elementListeners.forEach((listeners, element) => {
|
||||
this._elementListeners.set(
|
||||
element, listeners.filter(entry => { return entry.name != name; }));
|
||||
this._elementListeners.set(element, listeners.filter(entry => {
|
||||
return entry.name != name;
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
@ -372,7 +379,7 @@ export class AnimationTransitionNamespace {
|
||||
|
||||
const trigger = this._triggers[triggerName];
|
||||
const transition = trigger.fallbackTransition;
|
||||
const elementStates = this._engine.statesByElement.get(element) !;
|
||||
const elementStates = this._engine.statesByElement.get(element)!;
|
||||
const fromState = elementStates[triggerName] || DEFAULT_STATE_VALUE;
|
||||
const toState = new StateValue(VOID_VALUE);
|
||||
const player = new TransitionAnimationPlayer(this.id, triggerName, element);
|
||||
@ -448,7 +455,9 @@ export class AnimationTransitionNamespace {
|
||||
}
|
||||
}
|
||||
|
||||
insertNode(element: any, parent: any): void { addClass(element, this._hostClassName); }
|
||||
insertNode(element: any, parent: any): void {
|
||||
addClass(element, this._hostClassName);
|
||||
}
|
||||
|
||||
drainQueuedTransitions(microtaskId: number): QueueInstruction[] {
|
||||
const instructions: QueueInstruction[] = [];
|
||||
@ -538,7 +547,9 @@ export class TransitionAnimationEngine {
|
||||
public onRemovalComplete = (element: any, context: any) => {};
|
||||
|
||||
/** @internal */
|
||||
_onRemovalComplete(element: any, context: any) { this.onRemovalComplete(element, context); }
|
||||
_onRemovalComplete(element: any, context: any) {
|
||||
this.onRemovalComplete(element, context);
|
||||
}
|
||||
|
||||
constructor(
|
||||
public bodyNode: any, public driver: AnimationDriver,
|
||||
@ -631,7 +642,9 @@ export class TransitionAnimationEngine {
|
||||
this.afterFlushAnimationsDone(() => ns.destroy(context));
|
||||
}
|
||||
|
||||
private _fetchNamespace(id: string) { return this._namespaceLookup[id]; }
|
||||
private _fetchNamespace(id: string) {
|
||||
return this._namespaceLookup[id];
|
||||
}
|
||||
|
||||
fetchNamespacesByElement(element: any): Set<AnimationTransitionNamespace> {
|
||||
// normally there should only be one namespace per element, however
|
||||
@ -704,7 +717,9 @@ export class TransitionAnimationEngine {
|
||||
}
|
||||
}
|
||||
|
||||
collectEnterElement(element: any) { this.collectedEnterElements.push(element); }
|
||||
collectEnterElement(element: any) {
|
||||
this.collectedEnterElements.push(element);
|
||||
}
|
||||
|
||||
markElementAsDisabled(element: any, value: boolean) {
|
||||
if (value) {
|
||||
@ -740,11 +755,8 @@ export class TransitionAnimationEngine {
|
||||
|
||||
markElementAsRemoved(namespaceId: string, element: any, hasAnimation?: boolean, context?: any) {
|
||||
this.collectedLeaveElements.push(element);
|
||||
element[REMOVAL_FLAG] = {
|
||||
namespaceId,
|
||||
setForRemoval: context, hasAnimation,
|
||||
removedBeforeQueried: false
|
||||
};
|
||||
element[REMOVAL_FLAG] =
|
||||
{namespaceId, setForRemoval: context, hasAnimation, removedBeforeQueried: false};
|
||||
}
|
||||
|
||||
listen(
|
||||
@ -876,7 +888,9 @@ export class TransitionAnimationEngine {
|
||||
this._whenQuietFns = [];
|
||||
|
||||
if (players.length) {
|
||||
optimizeGroupPlayer(players).onDone(() => { quietFns.forEach(fn => fn()); });
|
||||
optimizeGroupPlayer(players).onDone(() => {
|
||||
quietFns.forEach(fn => fn());
|
||||
});
|
||||
} else {
|
||||
quietFns.forEach(fn => fn());
|
||||
}
|
||||
@ -950,16 +964,18 @@ export class TransitionAnimationEngine {
|
||||
|
||||
cleanupFns.push(() => {
|
||||
enterNodeMap.forEach((nodes, root) => {
|
||||
const className = enterNodeMapIds.get(root) !;
|
||||
const className = enterNodeMapIds.get(root)!;
|
||||
nodes.forEach(node => removeClass(node, className));
|
||||
});
|
||||
|
||||
leaveNodeMap.forEach((nodes, root) => {
|
||||
const className = leaveNodeMapIds.get(root) !;
|
||||
const className = leaveNodeMapIds.get(root)!;
|
||||
nodes.forEach(node => removeClass(node, className));
|
||||
});
|
||||
|
||||
allLeaveNodes.forEach(element => { this.processLeaveNode(element); });
|
||||
allLeaveNodes.forEach(element => {
|
||||
this.processLeaveNode(element);
|
||||
});
|
||||
});
|
||||
|
||||
const allPlayers: TransitionAnimationPlayer[] = [];
|
||||
@ -981,10 +997,10 @@ export class TransitionAnimationEngine {
|
||||
}
|
||||
|
||||
const nodeIsOrphaned = !bodyNode || !this.driver.containsElement(bodyNode, element);
|
||||
const leaveClassName = leaveNodeMapIds.get(element) !;
|
||||
const enterClassName = enterNodeMapIds.get(element) !;
|
||||
const leaveClassName = leaveNodeMapIds.get(element)!;
|
||||
const enterClassName = enterNodeMapIds.get(element)!;
|
||||
const instruction = this._buildInstruction(
|
||||
entry, subTimelines, enterClassName, leaveClassName, nodeIsOrphaned) !;
|
||||
entry, subTimelines, enterClassName, leaveClassName, nodeIsOrphaned)!;
|
||||
if (instruction.errors && instruction.errors.length) {
|
||||
erroneousTransitions.push(instruction);
|
||||
return;
|
||||
@ -1029,7 +1045,7 @@ export class TransitionAnimationEngine {
|
||||
instruction.preStyleProps.forEach((stringMap, element) => {
|
||||
const props = Object.keys(stringMap);
|
||||
if (props.length) {
|
||||
let setVal: Set<string> = allPreStyleElements.get(element) !;
|
||||
let setVal: Set<string> = allPreStyleElements.get(element)!;
|
||||
if (!setVal) {
|
||||
allPreStyleElements.set(element, setVal = new Set<string>());
|
||||
}
|
||||
@ -1039,7 +1055,7 @@ export class TransitionAnimationEngine {
|
||||
|
||||
instruction.postStyleProps.forEach((stringMap, element) => {
|
||||
const props = Object.keys(stringMap);
|
||||
let setVal: Set<string> = allPostStyleElements.get(element) !;
|
||||
let setVal: Set<string> = allPostStyleElements.get(element)!;
|
||||
if (!setVal) {
|
||||
allPostStyleElements.set(element, setVal = new Set<string>());
|
||||
}
|
||||
@ -1052,7 +1068,7 @@ export class TransitionAnimationEngine {
|
||||
const errors: string[] = [];
|
||||
erroneousTransitions.forEach(instruction => {
|
||||
errors.push(`@${instruction.triggerName} has failed due to:\n`);
|
||||
instruction.errors !.forEach(error => errors.push(`- ${error}\n`));
|
||||
instruction.errors!.forEach(error => errors.push(`- ${error}\n`));
|
||||
});
|
||||
|
||||
allPlayers.forEach(player => player.destroy());
|
||||
@ -1116,7 +1132,7 @@ export class TransitionAnimationEngine {
|
||||
replaceNodes.forEach(node => {
|
||||
const post = postStylesMap.get(node);
|
||||
const pre = preStylesMap.get(node);
|
||||
postStylesMap.set(node, { ...post, ...pre } as any);
|
||||
postStylesMap.set(node, {...post, ...pre} as any);
|
||||
});
|
||||
|
||||
const rootPlayers: TransitionAnimationPlayer[] = [];
|
||||
@ -1274,9 +1290,13 @@ export class TransitionAnimationEngine {
|
||||
return this._fetchNamespace(namespaceId).elementContainsData(element) || containsData;
|
||||
}
|
||||
|
||||
afterFlush(callback: () => any) { this._flushFns.push(callback); }
|
||||
afterFlush(callback: () => any) {
|
||||
this._flushFns.push(callback);
|
||||
}
|
||||
|
||||
afterFlushAnimationsDone(callback: () => any) { this._whenQuietFns.push(callback); }
|
||||
afterFlushAnimationsDone(callback: () => any) {
|
||||
this._whenQuietFns.push(callback);
|
||||
}
|
||||
|
||||
private _getPreviousPlayers(
|
||||
element: string, isQueriedElement: boolean, namespaceId?: string, triggerName?: string,
|
||||
@ -1413,8 +1433,9 @@ export class TransitionAnimationEngine {
|
||||
|
||||
// this basically makes all of the callbacks for sub element animations
|
||||
// be dependent on the upper players for when they finish
|
||||
allSubElements.forEach(
|
||||
element => { getOrSetAsInMap(skippedPlayersMap, element, []).push(player); });
|
||||
allSubElements.forEach(element => {
|
||||
getOrSetAsInMap(skippedPlayersMap, element, []).push(player);
|
||||
});
|
||||
|
||||
return player;
|
||||
}
|
||||
@ -1441,7 +1462,7 @@ export class TransitionAnimationPlayer implements AnimationPlayer {
|
||||
private _queuedCallbacks: {[name: string]: (() => any)[]} = {};
|
||||
public readonly destroyed = false;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public parentPlayer !: AnimationPlayer;
|
||||
public parentPlayer!: AnimationPlayer;
|
||||
|
||||
public markedForDestroy: boolean = false;
|
||||
public disabled = false;
|
||||
@ -1462,17 +1483,21 @@ export class TransitionAnimationPlayer implements AnimationPlayer {
|
||||
this._queuedCallbacks = {};
|
||||
this._containsRealPlayer = true;
|
||||
this.overrideTotalTime(player.totalTime);
|
||||
(this as{queued: boolean}).queued = false;
|
||||
(this as {queued: boolean}).queued = false;
|
||||
}
|
||||
|
||||
getRealPlayer() { return this._player; }
|
||||
getRealPlayer() {
|
||||
return this._player;
|
||||
}
|
||||
|
||||
overrideTotalTime(totalTime: number) { (this as any).totalTime = totalTime; }
|
||||
overrideTotalTime(totalTime: number) {
|
||||
(this as any).totalTime = totalTime;
|
||||
}
|
||||
|
||||
syncPlayerEvents(player: AnimationPlayer) {
|
||||
const p = this._player as any;
|
||||
if (p.triggerCallback) {
|
||||
player.onStart(() => p.triggerCallback !('start'));
|
||||
player.onStart(() => p.triggerCallback!('start'));
|
||||
}
|
||||
player.onDone(() => this.finish());
|
||||
player.onDestroy(() => this.destroy());
|
||||
@ -1503,24 +1528,38 @@ export class TransitionAnimationPlayer implements AnimationPlayer {
|
||||
this._player.onDestroy(fn);
|
||||
}
|
||||
|
||||
init(): void { this._player.init(); }
|
||||
init(): void {
|
||||
this._player.init();
|
||||
}
|
||||
|
||||
hasStarted(): boolean { return this.queued ? false : this._player.hasStarted(); }
|
||||
hasStarted(): boolean {
|
||||
return this.queued ? false : this._player.hasStarted();
|
||||
}
|
||||
|
||||
play(): void { !this.queued && this._player.play(); }
|
||||
play(): void {
|
||||
!this.queued && this._player.play();
|
||||
}
|
||||
|
||||
pause(): void { !this.queued && this._player.pause(); }
|
||||
pause(): void {
|
||||
!this.queued && this._player.pause();
|
||||
}
|
||||
|
||||
restart(): void { !this.queued && this._player.restart(); }
|
||||
restart(): void {
|
||||
!this.queued && this._player.restart();
|
||||
}
|
||||
|
||||
finish(): void { this._player.finish(); }
|
||||
finish(): void {
|
||||
this._player.finish();
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
(this as{destroyed: boolean}).destroyed = true;
|
||||
(this as {destroyed: boolean}).destroyed = true;
|
||||
this._player.destroy();
|
||||
}
|
||||
|
||||
reset(): void { !this.queued && this._player.reset(); }
|
||||
reset(): void {
|
||||
!this.queued && this._player.reset();
|
||||
}
|
||||
|
||||
setPosition(p: any): void {
|
||||
if (!this.queued) {
|
||||
@ -1528,7 +1567,9 @@ export class TransitionAnimationPlayer implements AnimationPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
getPosition(): number { return this.queued ? 0 : this._player.getPosition(); }
|
||||
getPosition(): number {
|
||||
return this.queued ? 0 : this._player.getPosition();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
triggerCallback(phaseName: string): void {
|
||||
@ -1539,7 +1580,7 @@ export class TransitionAnimationPlayer implements AnimationPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
function deleteOrUnsetInMap(map: Map<any, any[]>| {[key: string]: any}, key: any, value: any) {
|
||||
function deleteOrUnsetInMap(map: Map<any, any[]>|{[key: string]: any}, key: any, value: any) {
|
||||
let currentValues: any[]|null|undefined;
|
||||
if (map instanceof Map) {
|
||||
currentValues = map.get(key);
|
||||
@ -1661,7 +1702,7 @@ function buildRootMap(roots: any[], nodes: any[]): Map<any, any[]> {
|
||||
nodes.forEach(node => {
|
||||
const root = getRoot(node);
|
||||
if (root !== NULL_NODE) {
|
||||
rootMap.get(root) !.push(node);
|
||||
rootMap.get(root)!.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1742,7 +1783,7 @@ function replacePostStylesAsPre(
|
||||
|
||||
let preEntry = allPreStyleElements.get(element);
|
||||
if (preEntry) {
|
||||
postEntry.forEach(data => preEntry !.add(data));
|
||||
postEntry.forEach(data => preEntry!.add(data));
|
||||
} else {
|
||||
allPreStyleElements.set(element, postEntry);
|
||||
}
|
||||
|
@ -19,13 +19,17 @@ export class WebAnimationsDriver implements AnimationDriver {
|
||||
private _isNativeImpl = /\{\s*\[native\s+code\]\s*\}/.test(getElementAnimateFn().toString());
|
||||
private _cssKeyframesDriver = new CssKeyframesDriver();
|
||||
|
||||
validateStyleProperty(prop: string): boolean { return validateStyleProperty(prop); }
|
||||
validateStyleProperty(prop: string): boolean {
|
||||
return validateStyleProperty(prop);
|
||||
}
|
||||
|
||||
matchesElement(element: any, selector: string): boolean {
|
||||
return matchesElement(element, selector);
|
||||
}
|
||||
|
||||
containsElement(elm1: any, elm2: any): boolean { return containsElement(elm1, elm2); }
|
||||
containsElement(elm1: any, elm2: any): boolean {
|
||||
return containsElement(elm1, elm2);
|
||||
}
|
||||
|
||||
query(element: any, selector: string, multi: boolean): any[] {
|
||||
return invokeQuery(element, selector, multi);
|
||||
@ -35,7 +39,9 @@ export class WebAnimationsDriver implements AnimationDriver {
|
||||
return (window.getComputedStyle(element) as any)[prop] as string;
|
||||
}
|
||||
|
||||
overrideWebAnimationsSupport(supported: boolean) { this._isNativeImpl = supported; }
|
||||
overrideWebAnimationsSupport(supported: boolean) {
|
||||
this._isNativeImpl = supported;
|
||||
}
|
||||
|
||||
animate(
|
||||
element: any, keyframes: ɵStyleData[], duration: number, delay: number, easing: string,
|
||||
@ -47,7 +53,7 @@ export class WebAnimationsDriver implements AnimationDriver {
|
||||
}
|
||||
|
||||
const fill = delay == 0 ? 'both' : 'forwards';
|
||||
const playerOptions: {[key: string]: string | number} = {duration, delay, fill};
|
||||
const playerOptions: {[key: string]: string|number} = {duration, delay, fill};
|
||||
// we check for this to avoid having a null|undefined value be present
|
||||
// for the easing (which results in an error for certain browsers #9752)
|
||||
if (easing) {
|
||||
|
@ -23,18 +23,18 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||
private _started = false;
|
||||
private _destroyed = false;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
private _finalKeyframe !: {[key: string]: string | number};
|
||||
private _finalKeyframe!: {[key: string]: string|number};
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public readonly domPlayer !: DOMAnimation;
|
||||
public readonly domPlayer!: DOMAnimation;
|
||||
public time = 0;
|
||||
|
||||
public parentPlayer: AnimationPlayer|null = null;
|
||||
public currentSnapshot: {[styleName: string]: string | number} = {};
|
||||
public currentSnapshot: {[styleName: string]: string|number} = {};
|
||||
|
||||
constructor(
|
||||
public element: any, public keyframes: {[key: string]: string | number}[],
|
||||
public options: {[key: string]: string | number},
|
||||
public element: any, public keyframes: {[key: string]: string|number}[],
|
||||
public options: {[key: string]: string|number},
|
||||
private _specialStyles?: SpecialCasedStyles|null) {
|
||||
this._duration = <number>options['duration'];
|
||||
this._delay = <number>options['delay'] || 0;
|
||||
@ -59,7 +59,7 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||
this._initialized = true;
|
||||
|
||||
const keyframes = this.keyframes;
|
||||
(this as{domPlayer: DOMAnimation}).domPlayer =
|
||||
(this as {domPlayer: DOMAnimation}).domPlayer =
|
||||
this._triggerWebAnimation(this.element, keyframes, this.options);
|
||||
this._finalKeyframe = keyframes.length ? keyframes[keyframes.length - 1] : {};
|
||||
this.domPlayer.addEventListener('finish', () => this._onFinish());
|
||||
@ -81,11 +81,17 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||
return element['animate'](keyframes, options) as DOMAnimation;
|
||||
}
|
||||
|
||||
onStart(fn: () => void): void { this._onStartFns.push(fn); }
|
||||
onStart(fn: () => void): void {
|
||||
this._onStartFns.push(fn);
|
||||
}
|
||||
|
||||
onDone(fn: () => void): void { this._onDoneFns.push(fn); }
|
||||
onDone(fn: () => void): void {
|
||||
this._onDoneFns.push(fn);
|
||||
}
|
||||
|
||||
onDestroy(fn: () => void): void { this._onDestroyFns.push(fn); }
|
||||
onDestroy(fn: () => void): void {
|
||||
this._onDestroyFns.push(fn);
|
||||
}
|
||||
|
||||
play(): void {
|
||||
this._buildPlayer();
|
||||
@ -132,7 +138,9 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||
this.play();
|
||||
}
|
||||
|
||||
hasStarted(): boolean { return this._started; }
|
||||
hasStarted(): boolean {
|
||||
return this._started;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
if (!this._destroyed) {
|
||||
@ -147,14 +155,20 @@ export class WebAnimationsPlayer implements AnimationPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
setPosition(p: number): void { this.domPlayer.currentTime = p * this.time; }
|
||||
setPosition(p: number): void {
|
||||
this.domPlayer.currentTime = p * this.time;
|
||||
}
|
||||
|
||||
getPosition(): number { return this.domPlayer.currentTime / this.time; }
|
||||
getPosition(): number {
|
||||
return this.domPlayer.currentTime / this.time;
|
||||
}
|
||||
|
||||
get totalTime(): number { return this._delay + this._duration; }
|
||||
get totalTime(): number {
|
||||
return this._delay + this._duration;
|
||||
}
|
||||
|
||||
beforeDestroy() {
|
||||
const styles: {[key: string]: string | number} = {};
|
||||
const styles: {[key: string]: string|number} = {};
|
||||
if (this.hasStarted()) {
|
||||
Object.keys(this._finalKeyframe).forEach(prop => {
|
||||
if (prop != 'offset') {
|
||||
|
@ -23,7 +23,7 @@ export const NG_TRIGGER_SELECTOR = '.ng-trigger';
|
||||
export const NG_ANIMATING_CLASSNAME = 'ng-animating';
|
||||
export const NG_ANIMATING_SELECTOR = '.ng-animating';
|
||||
|
||||
export function resolveTimingValue(value: string | number) {
|
||||
export function resolveTimingValue(value: string|number) {
|
||||
if (typeof value == 'number') return value;
|
||||
|
||||
const matches = value.match(/^(-?[\.\d]+)(m?s)/);
|
||||
@ -42,14 +42,14 @@ function _convertTimeValueToMS(value: number, unit: string): number {
|
||||
}
|
||||
|
||||
export function resolveTiming(
|
||||
timings: string | number | AnimateTimings, errors: any[], allowNegativeValues?: boolean) {
|
||||
timings: string|number|AnimateTimings, errors: any[], allowNegativeValues?: boolean) {
|
||||
return timings.hasOwnProperty('duration') ?
|
||||
<AnimateTimings>timings :
|
||||
parseTimeExpression(<string|number>timings, errors, allowNegativeValues);
|
||||
}
|
||||
|
||||
function parseTimeExpression(
|
||||
exp: string | number, errors: string[], allowNegativeValues?: boolean): AnimateTimings {
|
||||
exp: string|number, errors: string[], allowNegativeValues?: boolean): AnimateTimings {
|
||||
const regex = /^(-?[\.\d]+)(m?s)(?:\s+(-?[\.\d]+)(m?s))?(?:\s+([-a-z]+(?:\(.+?\))?))?$/i;
|
||||
let duration: number;
|
||||
let delay: number = 0;
|
||||
@ -97,11 +97,13 @@ function parseTimeExpression(
|
||||
|
||||
export function copyObj(
|
||||
obj: {[key: string]: any}, destination: {[key: string]: any} = {}): {[key: string]: any} {
|
||||
Object.keys(obj).forEach(prop => { destination[prop] = obj[prop]; });
|
||||
Object.keys(obj).forEach(prop => {
|
||||
destination[prop] = obj[prop];
|
||||
});
|
||||
return destination;
|
||||
}
|
||||
|
||||
export function normalizeStyles(styles: ɵStyleData | ɵStyleData[]): ɵStyleData {
|
||||
export function normalizeStyles(styles: ɵStyleData|ɵStyleData[]): ɵStyleData {
|
||||
const normalizedStyles: ɵStyleData = {};
|
||||
if (Array.isArray(styles)) {
|
||||
styles.forEach(data => copyStyles(data, false, normalizedStyles));
|
||||
@ -186,8 +188,8 @@ export function eraseStyles(element: any, styles: ɵStyleData) {
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeAnimationEntry(steps: AnimationMetadata | AnimationMetadata[]):
|
||||
AnimationMetadata {
|
||||
export function normalizeAnimationEntry(steps: AnimationMetadata|
|
||||
AnimationMetadata[]): AnimationMetadata {
|
||||
if (Array.isArray(steps)) {
|
||||
if (steps.length == 1) return steps[0];
|
||||
return sequence(steps);
|
||||
@ -196,7 +198,7 @@ export function normalizeAnimationEntry(steps: AnimationMetadata | AnimationMeta
|
||||
}
|
||||
|
||||
export function validateStyleParams(
|
||||
value: string | number, options: AnimationOptions, errors: any[]) {
|
||||
value: string|number, options: AnimationOptions, errors: any[]) {
|
||||
const params = options.params || {};
|
||||
const matches = extractStyleParams(value);
|
||||
if (matches.length) {
|
||||
@ -211,7 +213,7 @@ export function validateStyleParams(
|
||||
|
||||
const PARAM_REGEX =
|
||||
new RegExp(`${SUBSTITUTION_EXPR_START}\\s*(.+?)\\s*${SUBSTITUTION_EXPR_END}`, 'g');
|
||||
export function extractStyleParams(value: string | number): string[] {
|
||||
export function extractStyleParams(value: string|number): string[] {
|
||||
let params: string[] = [];
|
||||
if (typeof value === 'string') {
|
||||
let match: any;
|
||||
@ -224,7 +226,7 @@ export function extractStyleParams(value: string | number): string[] {
|
||||
}
|
||||
|
||||
export function interpolateParams(
|
||||
value: string | number, params: {[name: string]: any}, errors: any[]): string|number {
|
||||
value: string|number, params: {[name: string]: any}, errors: any[]): string|number {
|
||||
const original = value.toString();
|
||||
const str = original.replace(PARAM_REGEX, (_, varName) => {
|
||||
let localVal = params[varName];
|
||||
@ -297,7 +299,9 @@ export function balancePreviousStylesIntoKeyframes(
|
||||
// tslint:disable-next-line
|
||||
for (var i = 1; i < keyframes.length; i++) {
|
||||
let kf = keyframes[i];
|
||||
missingStyleProps.forEach(function(prop) { kf[prop] = computeStyle(element, prop); });
|
||||
missingStyleProps.forEach(function(prop) {
|
||||
kf[prop] = computeStyle(element, prop);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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, AnimationMetadataType, AnimationOptions, animate, animation, group, keyframes, query, sequence, state, style, transition, trigger, useAnimation, ɵStyleData} from '@angular/animations';
|
||||
import {animate, animation, AnimationMetadata, AnimationMetadataType, AnimationOptions, AUTO_STYLE, group, keyframes, query, sequence, state, style, transition, trigger, useAnimation, ɵStyleData} from '@angular/animations';
|
||||
|
||||
import {Animation} from '../../src/dsl/animation';
|
||||
import {buildAnimationAst} from '../../src/dsl/animation_ast_builder';
|
||||
@ -35,7 +35,9 @@ function createDiv() {
|
||||
rootElement.appendChild(subElement2);
|
||||
});
|
||||
|
||||
afterEach(() => { document.body.removeChild(rootElement); });
|
||||
afterEach(() => {
|
||||
document.body.removeChild(rootElement);
|
||||
});
|
||||
|
||||
describe('validation', () => {
|
||||
it('should throw an error if one or more but not all keyframes() styles contain offsets',
|
||||
@ -45,7 +47,9 @@ function createDiv() {
|
||||
style({opacity: 1, offset: 1}),
|
||||
]));
|
||||
|
||||
expect(() => { validateAndThrowAnimationSequence(steps); })
|
||||
expect(() => {
|
||||
validateAndThrowAnimationSequence(steps);
|
||||
})
|
||||
.toThrowError(
|
||||
/Not all style\(\) steps within the declared keyframes\(\) contain offsets/);
|
||||
});
|
||||
@ -96,7 +100,9 @@ function createDiv() {
|
||||
]))
|
||||
]);
|
||||
|
||||
expect(() => { validateAndThrowAnimationSequence(steps); })
|
||||
expect(() => {
|
||||
validateAndThrowAnimationSequence(steps);
|
||||
})
|
||||
.toThrowError(
|
||||
/The CSS property "opacity" that exists between the times of "0ms" and "2000ms" is also being animated in a parallel animation between the times of "0ms" and "1500ms"/);
|
||||
});
|
||||
@ -191,7 +197,9 @@ function createDiv() {
|
||||
})),
|
||||
];
|
||||
|
||||
expect(() => { validateAndThrowAnimationSequence(steps); })
|
||||
expect(() => {
|
||||
validateAndThrowAnimationSequence(steps);
|
||||
})
|
||||
.toThrowError(
|
||||
/state\("final", ...\) must define default values for all the following style substitutions: one, two, three/);
|
||||
|
||||
@ -203,7 +211,9 @@ function createDiv() {
|
||||
}),
|
||||
{params: {redColor: 'maroon'}})];
|
||||
|
||||
expect(() => { validateAndThrowAnimationSequence(steps2); })
|
||||
expect(() => {
|
||||
validateAndThrowAnimationSequence(steps2);
|
||||
})
|
||||
.toThrowError(
|
||||
/state\("panfinal", ...\) must define default values for all the following style substitutions: greyColor/);
|
||||
});
|
||||
@ -211,7 +221,9 @@ function createDiv() {
|
||||
it('should throw an error if an invalid CSS property is used in the animation', () => {
|
||||
const steps = [animate(1000, style({abc: '500px'}))];
|
||||
|
||||
expect(() => { validateAndThrowAnimationSequence(steps); })
|
||||
expect(() => {
|
||||
validateAndThrowAnimationSequence(steps);
|
||||
})
|
||||
.toThrowError(
|
||||
/The provided animation property "abc" is not a supported CSS property for animations/);
|
||||
});
|
||||
@ -388,7 +400,7 @@ function createDiv() {
|
||||
let players = invokeAnimationSequence(rootElement, steps);
|
||||
expect(players.length).toEqual(1);
|
||||
|
||||
let p1 = players.pop() !;
|
||||
let p1 = players.pop()!;
|
||||
expect(p1.duration).toEqual(1500);
|
||||
expect(p1.keyframes).toEqual([
|
||||
{width: '*', offset: 0},
|
||||
@ -405,7 +417,7 @@ function createDiv() {
|
||||
players = invokeAnimationSequence(rootElement, steps);
|
||||
expect(players.length).toEqual(1);
|
||||
|
||||
p1 = players.pop() !;
|
||||
p1 = players.pop()!;
|
||||
expect(p1.duration).toEqual(1000);
|
||||
expect(p1.keyframes).toEqual([
|
||||
{width: '100px', offset: 0},
|
||||
@ -876,7 +888,9 @@ function createDiv() {
|
||||
const steps =
|
||||
[query('somethingFake', [style({opacity: 0}), animate(1000, style({opacity: 1}))])];
|
||||
|
||||
expect(() => { invokeAnimationSequence(rootElement, steps); })
|
||||
expect(() => {
|
||||
invokeAnimationSequence(rootElement, steps);
|
||||
})
|
||||
.toThrowError(
|
||||
/`query\("somethingFake"\)` returned zero elements\. \(Use `query\("somethingFake", \{ optional: true \}\)` if you wish to allow this\.\)/);
|
||||
});
|
||||
@ -887,13 +901,17 @@ function createDiv() {
|
||||
'somethingFake', [style({opacity: 0}), animate(1000, style({opacity: 1}))],
|
||||
{optional: true})];
|
||||
|
||||
expect(() => { invokeAnimationSequence(rootElement, steps); }).not.toThrow();
|
||||
expect(() => {
|
||||
invokeAnimationSequence(rootElement, steps);
|
||||
}).not.toThrow();
|
||||
|
||||
const steps2 = [query(
|
||||
'fakeSomethings', [style({opacity: 0}), animate(1000, style({opacity: 1}))],
|
||||
{optional: true})];
|
||||
|
||||
expect(() => { invokeAnimationSequence(rootElement, steps2); }).not.toThrow();
|
||||
expect(() => {
|
||||
invokeAnimationSequence(rootElement, steps2);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should delay the query operation if a delay option is provided', () => {
|
||||
@ -1025,8 +1043,7 @@ function createDiv() {
|
||||
|
||||
const players = invokeAnimationSequence(rootElement, steps, {}, fromStyles, toStyles);
|
||||
expect(players[0].keyframes).toEqual([
|
||||
{background: 'blue', offset: 0, easing: 'ease-out'},
|
||||
{background: 'red', offset: 1}
|
||||
{background: 'blue', offset: 0, easing: 'ease-out'}, {background: 'red', offset: 1}
|
||||
]);
|
||||
});
|
||||
});
|
||||
@ -1042,7 +1059,7 @@ function humanizeOffsets(keyframes: ɵStyleData[], digits: number = 3): ɵStyleD
|
||||
}
|
||||
|
||||
function invokeAnimationSequence(
|
||||
element: any, steps: AnimationMetadata | AnimationMetadata[], locals: {[key: string]: any} = {},
|
||||
element: any, steps: AnimationMetadata|AnimationMetadata[], locals: {[key: string]: any} = {},
|
||||
startingStyles: ɵStyleData[] = [], destinationStyles: ɵStyleData[] = [],
|
||||
subInstructions?: ElementInstructionMap): AnimationTimelineInstruction[] {
|
||||
const driver = new MockAnimationDriver();
|
||||
@ -1050,7 +1067,7 @@ function invokeAnimationSequence(
|
||||
.buildTimelines(element, startingStyles, destinationStyles, locals, subInstructions);
|
||||
}
|
||||
|
||||
function validateAndThrowAnimationSequence(steps: AnimationMetadata | AnimationMetadata[]) {
|
||||
function validateAndThrowAnimationSequence(steps: AnimationMetadata|AnimationMetadata[]) {
|
||||
const driver = new MockAnimationDriver();
|
||||
const errors: any[] = [];
|
||||
const ast = buildAnimationAst(driver, steps, errors);
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AnimationOptions, animate, state, style, transition} from '@angular/animations';
|
||||
import {animate, AnimationOptions, state, style, transition} from '@angular/animations';
|
||||
import {AnimationTransitionInstruction} from '@angular/animations/browser/src/dsl/animation_transition_instruction';
|
||||
import {AnimationTrigger} from '@angular/animations/browser/src/dsl/animation_trigger';
|
||||
|
||||
@ -25,7 +25,9 @@ import {makeTrigger} from '../shared';
|
||||
document.body.appendChild(element);
|
||||
});
|
||||
|
||||
afterEach(() => { document.body.removeChild(element); });
|
||||
afterEach(() => {
|
||||
document.body.removeChild(element);
|
||||
});
|
||||
|
||||
describe('trigger validation', () => {
|
||||
it('should group errors together for an animation trigger', () => {
|
||||
@ -36,8 +38,9 @@ import {makeTrigger} from '../shared';
|
||||
|
||||
it('should throw an error when a transition within a trigger contains an invalid expression',
|
||||
() => {
|
||||
expect(
|
||||
() => { makeTrigger('name', [transition('somethingThatIsWrong', animate(3333))]); })
|
||||
expect(() => {
|
||||
makeTrigger('name', [transition('somethingThatIsWrong', animate(3333))]);
|
||||
})
|
||||
.toThrowError(
|
||||
/- The provided transition expression "somethingThatIsWrong" is not supported/);
|
||||
});
|
||||
@ -78,7 +81,7 @@ import {makeTrigger} from '../shared';
|
||||
const result = makeTrigger(
|
||||
'name', [transition('a => b', animate(1234)), transition('b => c', animate(5678))]);
|
||||
|
||||
const trans = buildTransition(result, element, 'b', 'c') !;
|
||||
const trans = buildTransition(result, element, 'b', 'c')!;
|
||||
expect(trans.timelines.length).toEqual(1);
|
||||
const timeline = trans.timelines[0];
|
||||
expect(timeline.duration).toEqual(5678);
|
||||
@ -90,13 +93,13 @@ import {makeTrigger} from '../shared';
|
||||
transition('* => *', animate(9999))
|
||||
]);
|
||||
|
||||
let trans = buildTransition(result, element, 'b', 'c') !;
|
||||
let trans = buildTransition(result, element, 'b', 'c')!;
|
||||
expect(trans.timelines[0].duration).toEqual(5678);
|
||||
|
||||
trans = buildTransition(result, element, 'a', 'b') !;
|
||||
trans = buildTransition(result, element, 'a', 'b')!;
|
||||
expect(trans.timelines[0].duration).toEqual(1234);
|
||||
|
||||
trans = buildTransition(result, element, 'c', 'c') !;
|
||||
trans = buildTransition(result, element, 'c', 'c')!;
|
||||
expect(trans.timelines[0].duration).toEqual(9999);
|
||||
});
|
||||
|
||||
@ -110,23 +113,23 @@ import {makeTrigger} from '../shared';
|
||||
it('should support bi-directional transition expressions', () => {
|
||||
const result = makeTrigger('name', [transition('a <=> b', animate(2222))]);
|
||||
|
||||
const t1 = buildTransition(result, element, 'a', 'b') !;
|
||||
const t1 = buildTransition(result, element, 'a', 'b')!;
|
||||
expect(t1.timelines[0].duration).toEqual(2222);
|
||||
|
||||
const t2 = buildTransition(result, element, 'b', 'a') !;
|
||||
const t2 = buildTransition(result, element, 'b', 'a')!;
|
||||
expect(t2.timelines[0].duration).toEqual(2222);
|
||||
});
|
||||
|
||||
it('should support multiple transition statements in one string', () => {
|
||||
const result = makeTrigger('name', [transition('a => b, b => a, c => *', animate(1234))]);
|
||||
|
||||
const t1 = buildTransition(result, element, 'a', 'b') !;
|
||||
const t1 = buildTransition(result, element, 'a', 'b')!;
|
||||
expect(t1.timelines[0].duration).toEqual(1234);
|
||||
|
||||
const t2 = buildTransition(result, element, 'b', 'a') !;
|
||||
const t2 = buildTransition(result, element, 'b', 'a')!;
|
||||
expect(t2.timelines[0].duration).toEqual(1234);
|
||||
|
||||
const t3 = buildTransition(result, element, 'c', 'a') !;
|
||||
const t3 = buildTransition(result, element, 'c', 'a')!;
|
||||
expect(t3.timelines[0].duration).toEqual(1234);
|
||||
});
|
||||
|
||||
@ -138,7 +141,7 @@ import {makeTrigger} from '../shared';
|
||||
'a => b', [style({height: '{{ a }}'}), animate(1000, style({height: '{{ b }}'}))],
|
||||
buildParams({a: '100px', b: '200px'}))]);
|
||||
|
||||
const trans = buildTransition(result, element, 'a', 'b') !;
|
||||
const trans = buildTransition(result, element, 'a', 'b')!;
|
||||
const keyframes = trans.timelines[0].keyframes;
|
||||
expect(keyframes).toEqual([{height: '100px', offset: 0}, {height: '200px', offset: 1}]);
|
||||
});
|
||||
@ -153,7 +156,7 @@ import {makeTrigger} from '../shared';
|
||||
buildParams({a: '100px', b: '200px'}))]);
|
||||
|
||||
const trans =
|
||||
buildTransition(result, element, 'a', 'b', {}, buildParams({a: '300px'})) !;
|
||||
buildTransition(result, element, 'a', 'b', {}, buildParams({a: '300px'}))!;
|
||||
|
||||
const keyframes = trans.timelines[0].keyframes;
|
||||
expect(keyframes).toEqual(
|
||||
@ -167,7 +170,7 @@ import {makeTrigger} from '../shared';
|
||||
transition('true <=> false', animate(1234))
|
||||
]);
|
||||
|
||||
const trans = buildTransition(result, element, false, true) !;
|
||||
const trans = buildTransition(result, element, false, true)!;
|
||||
expect(trans.timelines[0].duration).toEqual(1234);
|
||||
});
|
||||
|
||||
@ -177,7 +180,7 @@ import {makeTrigger} from '../shared';
|
||||
transition('1 <=> 0', animate(4567))
|
||||
]);
|
||||
|
||||
const trans = buildTransition(result, element, false, true) !;
|
||||
const trans = buildTransition(result, element, false, true)!;
|
||||
expect(trans.timelines[0].duration).toEqual(4567);
|
||||
});
|
||||
|
||||
@ -188,7 +191,7 @@ import {makeTrigger} from '../shared';
|
||||
transition('1 <=> 0', animate(4567))
|
||||
]);
|
||||
|
||||
const trans = buildTransition(result, element, false, true) !;
|
||||
const trans = buildTransition(result, element, false, true)!;
|
||||
expect(trans.timelines[0].keyframes).toEqual([
|
||||
{offset: 0, color: 'red'}, {offset: 1, color: 'green'}
|
||||
]);
|
||||
@ -201,7 +204,7 @@ import {makeTrigger} from '../shared';
|
||||
transition('true <=> false', animate(4567))
|
||||
]);
|
||||
|
||||
const trans = buildTransition(result, element, false, true) !;
|
||||
const trans = buildTransition(result, element, false, true)!;
|
||||
expect(trans.timelines[0].keyframes).toEqual([
|
||||
{offset: 0, color: 'orange'}, {offset: 1, color: 'blue'}
|
||||
]);
|
||||
@ -214,7 +217,7 @@ import {makeTrigger} from '../shared';
|
||||
]);
|
||||
|
||||
expect(() => {
|
||||
const trans = buildTransition(result, element, false, true) !;
|
||||
const trans = buildTransition(result, element, false, true)!;
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
@ -222,14 +225,14 @@ import {makeTrigger} from '../shared';
|
||||
it('should alias the :enter transition as void => *', () => {
|
||||
const result = makeTrigger('name', [transition(':enter', animate(3333))]);
|
||||
|
||||
const trans = buildTransition(result, element, 'void', 'something') !;
|
||||
const trans = buildTransition(result, element, 'void', 'something')!;
|
||||
expect(trans.timelines[0].duration).toEqual(3333);
|
||||
});
|
||||
|
||||
it('should alias the :leave transition as * => void', () => {
|
||||
const result = makeTrigger('name', [transition(':leave', animate(3333))]);
|
||||
|
||||
const trans = buildTransition(result, element, 'something', 'void') !;
|
||||
const trans = buildTransition(result, element, 'something', 'void')!;
|
||||
expect(trans.timelines[0].duration).toEqual(3333);
|
||||
});
|
||||
});
|
||||
@ -242,12 +245,12 @@ function buildTransition(
|
||||
fromOptions?: AnimationOptions, toOptions?: AnimationOptions): AnimationTransitionInstruction|
|
||||
null {
|
||||
const params = toOptions && toOptions.params || {};
|
||||
const trans = trigger.matchTransition(fromState, toState, element, params) !;
|
||||
const trans = trigger.matchTransition(fromState, toState, element, params)!;
|
||||
if (trans) {
|
||||
const driver = new MockAnimationDriver();
|
||||
return trans.build(
|
||||
driver, element, fromState, toState, ENTER_CLASSNAME, LEAVE_CLASSNAME, fromOptions,
|
||||
toOptions) !;
|
||||
toOptions)!;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -16,13 +16,13 @@ import {WebAnimationsStyleNormalizer} from '../../../src/dsl/style_normalization
|
||||
expect(normalizer.normalizePropertyName('width', [])).toEqual('width');
|
||||
expect(normalizer.normalizePropertyName('border-width', [])).toEqual('borderWidth');
|
||||
expect(normalizer.normalizePropertyName('borderHeight', [])).toEqual('borderHeight');
|
||||
expect(normalizer.normalizePropertyName('-webkit-animation', [
|
||||
])).toEqual('WebkitAnimation');
|
||||
expect(normalizer.normalizePropertyName('-webkit-animation', []))
|
||||
.toEqual('WebkitAnimation');
|
||||
});
|
||||
});
|
||||
|
||||
describe('normalizeStyleValue', () => {
|
||||
function normalize(prop: string, val: string | number): string {
|
||||
function normalize(prop: string, val: string|number): string {
|
||||
const errors: string[] = [];
|
||||
const result = normalizer.normalizeStyleValue(prop, prop, val, errors);
|
||||
if (errors.length) {
|
||||
|
@ -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 {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing';
|
||||
import {fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {CssKeyframesDriver} from '../../../src/render/css_keyframes/css_keyframes_driver';
|
||||
import {CssKeyframesPlayer} from '../../../src/render/css_keyframes/css_keyframes_player';
|
||||
@ -16,7 +16,7 @@ import {assertElementExistsInDom, createElement, findKeyframeDefinition, forceRe
|
||||
const CSS_KEYFRAME_RULE_TYPE = 7;
|
||||
|
||||
describe('CssKeyframesDriver tests', () => {
|
||||
if (isNode || typeof(window as any)['AnimationEvent'] == 'undefined') return;
|
||||
if (isNode || typeof (window as any)['AnimationEvent'] == 'undefined') return;
|
||||
|
||||
describe('building keyframes', () => {
|
||||
it('should build CSS keyframe style object containing the keyframe styles', () => {
|
||||
@ -28,7 +28,7 @@ describe('CssKeyframesDriver tests', () => {
|
||||
{opacity: 1, width: '200px', offset: 1},
|
||||
]);
|
||||
|
||||
const head = document.querySelector('head') !;
|
||||
const head = document.querySelector('head')!;
|
||||
head.appendChild(kfElm);
|
||||
forceReflow();
|
||||
|
||||
@ -67,7 +67,7 @@ describe('CssKeyframesDriver tests', () => {
|
||||
{width: '200px', offset: 1},
|
||||
]);
|
||||
|
||||
const head = document.querySelector('head') !;
|
||||
const head = document.querySelector('head')!;
|
||||
head.appendChild(kfElm);
|
||||
forceReflow();
|
||||
|
||||
@ -261,7 +261,7 @@ describe('CssKeyframesDriver tests', () => {
|
||||
|
||||
player.play();
|
||||
player.finish();
|
||||
player.beforeDestroy !();
|
||||
player.beforeDestroy!();
|
||||
expect(player.currentSnapshot).toEqual({
|
||||
width: '999px',
|
||||
height: '999px',
|
||||
|
@ -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 {TestBed, fakeAsync, flushMicrotasks} from '@angular/core/testing';
|
||||
import {fakeAsync, flushMicrotasks, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {DirectStylePlayer} from '../../../src/render/css_keyframes/direct_style_player';
|
||||
|
||||
|
@ -13,7 +13,7 @@ import {assertStyle, createElement, makeAnimationEvent, supportsAnimationEventCr
|
||||
const EMPTY_FN = () => {};
|
||||
{
|
||||
describe('ElementAnimationStyleHandler', () => {
|
||||
if (isNode || typeof(window as any)['AnimationEvent'] == 'undefined') return;
|
||||
if (isNode || typeof (window as any)['AnimationEvent'] == 'undefined') return;
|
||||
|
||||
it('should add and remove an animation on to an element\'s styling', () => {
|
||||
const element = createElement();
|
||||
|
@ -10,7 +10,7 @@ export function forceReflow() {
|
||||
}
|
||||
|
||||
export function makeAnimationEvent(
|
||||
startOrEnd: 'start' | 'end', animationName: string, elapsedTime: number, timestamp?: number) {
|
||||
startOrEnd: 'start'|'end', animationName: string, elapsedTime: number, timestamp?: number) {
|
||||
const e = new AnimationEvent('animation' + startOrEnd, {animationName, elapsedTime});
|
||||
if (timestamp) {
|
||||
(e as any)._ngTestManualTimestamp = timestamp;
|
||||
|
@ -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, animate, style} from '@angular/animations';
|
||||
import {animate, AnimationMetadata, style} from '@angular/animations';
|
||||
|
||||
import {AnimationStyleNormalizer, NoopAnimationStyleNormalizer} from '../../src/dsl/style_normalization/animation_style_normalizer';
|
||||
import {AnimationDriver} from '../../src/render/animation_driver';
|
||||
@ -14,98 +14,101 @@ import {TimelineAnimationEngine} from '../../src/render/timeline_animation_engin
|
||||
import {MockAnimationDriver, MockAnimationPlayer} from '../../testing/src/mock_animation_driver';
|
||||
|
||||
(function() {
|
||||
const defaultDriver = new MockAnimationDriver();
|
||||
const defaultDriver = new MockAnimationDriver();
|
||||
|
||||
function makeEngine(body: any, driver?: AnimationDriver, normalizer?: AnimationStyleNormalizer) {
|
||||
return new TimelineAnimationEngine(
|
||||
body, driver || defaultDriver, normalizer || new NoopAnimationStyleNormalizer());
|
||||
}
|
||||
function makeEngine(body: any, driver?: AnimationDriver, normalizer?: AnimationStyleNormalizer) {
|
||||
return new TimelineAnimationEngine(
|
||||
body, driver || defaultDriver, normalizer || new NoopAnimationStyleNormalizer());
|
||||
}
|
||||
|
||||
// these tests are only mean't to be run within the DOM
|
||||
if (isNode) return;
|
||||
// these tests are only mean't to be run within the DOM
|
||||
if (isNode) return;
|
||||
|
||||
describe('TimelineAnimationEngine', () => {
|
||||
let element: any;
|
||||
describe('TimelineAnimationEngine', () => {
|
||||
let element: any;
|
||||
|
||||
beforeEach(() => {
|
||||
MockAnimationDriver.log = [];
|
||||
element = document.createElement('div');
|
||||
document.body.appendChild(element);
|
||||
});
|
||||
|
||||
afterEach(() => document.body.removeChild(element));
|
||||
|
||||
it('should animate a timeline', () => {
|
||||
const engine = makeEngine(getBodyNode());
|
||||
const steps = [style({height: 100}), animate(1000, style({height: 0}))];
|
||||
expect(MockAnimationDriver.log.length).toEqual(0);
|
||||
invokeAnimation(engine, element, steps);
|
||||
expect(MockAnimationDriver.log.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should not destroy timeline-based animations after they have finished', () => {
|
||||
const engine = makeEngine(getBodyNode());
|
||||
|
||||
const log: string[] = [];
|
||||
function capture(value: string) {
|
||||
return () => { log.push(value); };
|
||||
}
|
||||
|
||||
const steps = [style({height: 0}), animate(1000, style({height: 500}))];
|
||||
|
||||
const player = invokeAnimation(engine, element, steps);
|
||||
player.onDone(capture('done'));
|
||||
player.onDestroy(capture('destroy'));
|
||||
expect(log).toEqual([]);
|
||||
|
||||
player.finish();
|
||||
expect(log).toEqual(['done']);
|
||||
|
||||
player.destroy();
|
||||
expect(log).toEqual(['done', 'destroy']);
|
||||
});
|
||||
|
||||
it('should normalize the style values that are animateTransitioned within an a timeline animation',
|
||||
() => {
|
||||
const engine =
|
||||
makeEngine(getBodyNode(), defaultDriver, new SuffixNormalizer('-normalized'));
|
||||
|
||||
const steps = [
|
||||
style({width: '333px'}),
|
||||
animate(1000, style({width: '999px'})),
|
||||
];
|
||||
|
||||
const player = invokeAnimation(engine, element, steps) as MockAnimationPlayer;
|
||||
expect(player.keyframes).toEqual([
|
||||
{'width-normalized': '333px-normalized', offset: 0},
|
||||
{'width-normalized': '999px-normalized', offset: 1}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should normalize `*` values', () => {
|
||||
const driver = new SuperMockDriver();
|
||||
const engine = makeEngine(getBodyNode(), driver);
|
||||
|
||||
const steps = [
|
||||
style({width: '*'}),
|
||||
animate(1000, style({width: '999px'})),
|
||||
];
|
||||
|
||||
const player = invokeAnimation(engine, element, steps) as MockAnimationPlayer;
|
||||
expect(player.keyframes).toEqual([{width: '*star*', offset: 0}, {width: '999px', offset: 1}]);
|
||||
});
|
||||
beforeEach(() => {
|
||||
MockAnimationDriver.log = [];
|
||||
element = document.createElement('div');
|
||||
document.body.appendChild(element);
|
||||
});
|
||||
|
||||
afterEach(() => document.body.removeChild(element));
|
||||
|
||||
it('should animate a timeline', () => {
|
||||
const engine = makeEngine(getBodyNode());
|
||||
const steps = [style({height: 100}), animate(1000, style({height: 0}))];
|
||||
expect(MockAnimationDriver.log.length).toEqual(0);
|
||||
invokeAnimation(engine, element, steps);
|
||||
expect(MockAnimationDriver.log.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should not destroy timeline-based animations after they have finished', () => {
|
||||
const engine = makeEngine(getBodyNode());
|
||||
|
||||
const log: string[] = [];
|
||||
function capture(value: string) {
|
||||
return () => {
|
||||
log.push(value);
|
||||
};
|
||||
}
|
||||
|
||||
const steps = [style({height: 0}), animate(1000, style({height: 500}))];
|
||||
|
||||
const player = invokeAnimation(engine, element, steps);
|
||||
player.onDone(capture('done'));
|
||||
player.onDestroy(capture('destroy'));
|
||||
expect(log).toEqual([]);
|
||||
|
||||
player.finish();
|
||||
expect(log).toEqual(['done']);
|
||||
|
||||
player.destroy();
|
||||
expect(log).toEqual(['done', 'destroy']);
|
||||
});
|
||||
|
||||
it('should normalize the style values that are animateTransitioned within an a timeline animation',
|
||||
() => {
|
||||
const engine = makeEngine(getBodyNode(), defaultDriver, new SuffixNormalizer('-normalized'));
|
||||
|
||||
const steps = [
|
||||
style({width: '333px'}),
|
||||
animate(1000, style({width: '999px'})),
|
||||
];
|
||||
|
||||
const player = invokeAnimation(engine, element, steps) as MockAnimationPlayer;
|
||||
expect(player.keyframes).toEqual([
|
||||
{'width-normalized': '333px-normalized', offset: 0},
|
||||
{'width-normalized': '999px-normalized', offset: 1}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should normalize `*` values', () => {
|
||||
const driver = new SuperMockDriver();
|
||||
const engine = makeEngine(getBodyNode(), driver);
|
||||
|
||||
const steps = [
|
||||
style({width: '*'}),
|
||||
animate(1000, style({width: '999px'})),
|
||||
];
|
||||
|
||||
const player = invokeAnimation(engine, element, steps) as MockAnimationPlayer;
|
||||
expect(player.keyframes).toEqual([{width: '*star*', offset: 0}, {width: '999px', offset: 1}]);
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
function invokeAnimation(
|
||||
engine: TimelineAnimationEngine, element: any, steps: AnimationMetadata | AnimationMetadata[],
|
||||
engine: TimelineAnimationEngine, element: any, steps: AnimationMetadata|AnimationMetadata[],
|
||||
id: string = 'id') {
|
||||
engine.register(id, steps);
|
||||
return engine.create(id, element);
|
||||
}
|
||||
|
||||
class SuffixNormalizer extends AnimationStyleNormalizer {
|
||||
constructor(private _suffix: string) { super(); }
|
||||
constructor(private _suffix: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
normalizePropertyName(propertyName: string, errors: string[]): string {
|
||||
return propertyName + this._suffix;
|
||||
@ -119,5 +122,7 @@ class SuffixNormalizer extends AnimationStyleNormalizer {
|
||||
}
|
||||
|
||||
class SuperMockDriver extends MockAnimationDriver {
|
||||
computeStyle(element: any, prop: string, defaultValue?: string): string { return '*star*'; }
|
||||
computeStyle(element: any, prop: string, defaultValue?: string): string {
|
||||
return '*star*';
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,9 @@ import {WebAnimationsPlayer} from '../../../src/render/web_animations/web_animat
|
||||
let innerPlayer: MockDomAnimation|null = null;
|
||||
beforeEach(() => {
|
||||
element = {};
|
||||
element['animate'] = () => { return innerPlayer = new MockDomAnimation(); };
|
||||
element['animate'] = () => {
|
||||
return innerPlayer = new MockDomAnimation();
|
||||
};
|
||||
});
|
||||
|
||||
describe('WebAnimationsPlayer tests', () => {
|
||||
@ -26,7 +28,7 @@ import {WebAnimationsPlayer} from '../../../src/render/web_animations/web_animat
|
||||
const player = new WebAnimationsPlayer(element, keyframes, {duration: 1000});
|
||||
|
||||
player.init();
|
||||
const p = innerPlayer !;
|
||||
const p = innerPlayer!;
|
||||
expect(p.log).toEqual(['pause']);
|
||||
|
||||
player.play();
|
||||
@ -42,7 +44,7 @@ import {WebAnimationsPlayer} from '../../../src/render/web_animations/web_animat
|
||||
const player = new WebAnimationsPlayer(element, keyframes, {duration: 1000});
|
||||
|
||||
player.play();
|
||||
const p = innerPlayer !;
|
||||
const p = innerPlayer!;
|
||||
expect(p.log).toEqual(['play']);
|
||||
});
|
||||
|
||||
@ -70,10 +72,18 @@ import {WebAnimationsPlayer} from '../../../src/render/web_animations/web_animat
|
||||
|
||||
class MockDomAnimation implements DOMAnimation {
|
||||
log: string[] = [];
|
||||
cancel(): void { this.log.push('cancel'); }
|
||||
play(): void { this.log.push('play'); }
|
||||
pause(): void { this.log.push('pause'); }
|
||||
finish(): void { this.log.push('finish'); }
|
||||
cancel(): void {
|
||||
this.log.push('cancel');
|
||||
}
|
||||
play(): void {
|
||||
this.log.push('play');
|
||||
}
|
||||
pause(): void {
|
||||
this.log.push('pause');
|
||||
}
|
||||
finish(): void {
|
||||
this.log.push('finish');
|
||||
}
|
||||
onfinish: Function = () => {};
|
||||
position: number = 0;
|
||||
currentTime: number = 0;
|
||||
|
@ -21,8 +21,8 @@ export function makeTrigger(
|
||||
const triggerAst = buildAnimationAst(driver, triggerData, errors) as TriggerAst;
|
||||
if (!skipErrors && errors.length) {
|
||||
const LINE_START = '\n - ';
|
||||
throw new Error(
|
||||
`Animation parsing for the ${name} trigger have failed:${LINE_START}${errors.join(LINE_START)}`);
|
||||
throw new Error(`Animation parsing for the ${name} trigger have failed:${LINE_START}${
|
||||
errors.join(LINE_START)}`);
|
||||
}
|
||||
return buildTrigger(name, triggerAst);
|
||||
}
|
||||
|
@ -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, AnimationPlayer, NoopAnimationPlayer, ɵStyleData} from '@angular/animations';
|
||||
import {AnimationPlayer, AUTO_STYLE, NoopAnimationPlayer, ɵStyleData} from '@angular/animations';
|
||||
import {AnimationDriver, ɵallowPreviousPlayerStylesMerge as allowPreviousPlayerStylesMerge, ɵcontainsElement as containsElement, ɵinvokeQuery as invokeQuery, ɵmatchesElement as matchesElement, ɵvalidateStyleProperty as validateStyleProperty} from '@angular/animations/browser';
|
||||
|
||||
|
||||
@ -15,13 +15,17 @@ import {AnimationDriver, ɵallowPreviousPlayerStylesMerge as allowPreviousPlayer
|
||||
export class MockAnimationDriver implements AnimationDriver {
|
||||
static log: AnimationPlayer[] = [];
|
||||
|
||||
validateStyleProperty(prop: string): boolean { return validateStyleProperty(prop); }
|
||||
validateStyleProperty(prop: string): boolean {
|
||||
return validateStyleProperty(prop);
|
||||
}
|
||||
|
||||
matchesElement(element: any, selector: string): boolean {
|
||||
return matchesElement(element, selector);
|
||||
}
|
||||
|
||||
containsElement(elm1: any, elm2: any): boolean { return containsElement(elm1, elm2); }
|
||||
containsElement(elm1: any, elm2: any): boolean {
|
||||
return containsElement(elm1, elm2);
|
||||
}
|
||||
|
||||
query(element: any, selector: string, multi: boolean): any[] {
|
||||
return invokeQuery(element, selector, multi);
|
||||
@ -32,7 +36,7 @@ export class MockAnimationDriver implements AnimationDriver {
|
||||
}
|
||||
|
||||
animate(
|
||||
element: any, keyframes: {[key: string]: string | number}[], duration: number, delay: number,
|
||||
element: any, keyframes: {[key: string]: string|number}[], duration: number, delay: number,
|
||||
easing: string, previousPlayers: any[] = []): MockAnimationPlayer {
|
||||
const player =
|
||||
new MockAnimationPlayer(element, keyframes, duration, delay, easing, previousPlayers);
|
||||
@ -47,12 +51,12 @@ export class MockAnimationDriver implements AnimationDriver {
|
||||
export class MockAnimationPlayer extends NoopAnimationPlayer {
|
||||
private __finished = false;
|
||||
private __started = false;
|
||||
public previousStyles: {[key: string]: string | number} = {};
|
||||
public previousStyles: {[key: string]: string|number} = {};
|
||||
private _onInitFns: (() => any)[] = [];
|
||||
public currentSnapshot: ɵStyleData = {};
|
||||
|
||||
constructor(
|
||||
public element: any, public keyframes: {[key: string]: string | number}[],
|
||||
public element: any, public keyframes: {[key: string]: string|number}[],
|
||||
public duration: number, public delay: number, public easing: string,
|
||||
public previousPlayers: any[]) {
|
||||
super(duration, delay);
|
||||
@ -68,7 +72,9 @@ export class MockAnimationPlayer extends NoopAnimationPlayer {
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
onInit(fn: () => any) { this._onInitFns.push(fn); }
|
||||
onInit(fn: () => any) {
|
||||
this._onInitFns.push(fn);
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
init() {
|
||||
@ -95,7 +101,9 @@ export class MockAnimationPlayer extends NoopAnimationPlayer {
|
||||
this.__started = true;
|
||||
}
|
||||
|
||||
hasStarted() { return this.__started; }
|
||||
hasStarted() {
|
||||
return this.__started;
|
||||
}
|
||||
|
||||
beforeDestroy() {
|
||||
const captures: ɵStyleData = {};
|
||||
|
@ -9,7 +9,9 @@
|
||||
/**
|
||||
* Represents a set of CSS styles for use in an animation style.
|
||||
*/
|
||||
export interface ɵStyleData { [key: string]: string|number; }
|
||||
export interface ɵStyleData {
|
||||
[key: string]: string|number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents animation-step timing parameters for an animation step.
|
||||
@ -67,10 +69,10 @@ export declare interface AnimationOptions {
|
||||
*/
|
||||
delay?: number|string;
|
||||
/**
|
||||
* A set of developer-defined parameters that modify styling and timing
|
||||
* when an animation action starts. An array of key-value pairs, where the provided value
|
||||
* is used as a default.
|
||||
*/
|
||||
* A set of developer-defined parameters that modify styling and timing
|
||||
* when an animation action starts. An array of key-value pairs, where the provided value
|
||||
* is used as a default.
|
||||
*/
|
||||
params?: {[name: string]: any};
|
||||
}
|
||||
|
||||
@ -81,7 +83,9 @@ export declare interface AnimationOptions {
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export declare interface AnimateChildOptions extends AnimationOptions { duration?: number|string; }
|
||||
export declare interface AnimateChildOptions extends AnimationOptions {
|
||||
duration?: number|string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Constants for the categories of parameters that can be defined for animations.
|
||||
@ -171,7 +175,9 @@ export const AUTO_STYLE = '*';
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export interface AnimationMetadata { type: AnimationMetadataType; }
|
||||
export interface AnimationMetadata {
|
||||
type: AnimationMetadataType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains an animation trigger. Instantiated and returned by the
|
||||
@ -181,8 +187,8 @@ export interface AnimationMetadata { type: AnimationMetadataType; }
|
||||
*/
|
||||
export interface AnimationTriggerMetadata extends AnimationMetadata {
|
||||
/**
|
||||
* The trigger name, used to associate it with an element. Unique within the component.
|
||||
*/
|
||||
* The trigger name, used to associate it with an element. Unique within the component.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* An animation definition object, containing an array of state and transition declarations.
|
||||
@ -654,8 +660,9 @@ export function trigger(name: string, definitions: AnimationMetadata[]): Animati
|
||||
* @publicApi
|
||||
*/
|
||||
export function animate(
|
||||
timings: string | number, styles: AnimationStyleMetadata | AnimationKeyframesSequenceMetadata |
|
||||
null = null): AnimationAnimateMetadata {
|
||||
timings: string|number,
|
||||
styles: AnimationStyleMetadata|AnimationKeyframesSequenceMetadata|null =
|
||||
null): AnimationAnimateMetadata {
|
||||
return {type: AnimationMetadataType.Animate, styles, timings};
|
||||
}
|
||||
|
||||
@ -693,7 +700,7 @@ export function animate(
|
||||
* @publicApi
|
||||
*/
|
||||
export function group(
|
||||
steps: AnimationMetadata[], options: AnimationOptions | null = null): AnimationGroupMetadata {
|
||||
steps: AnimationMetadata[], options: AnimationOptions|null = null): AnimationGroupMetadata {
|
||||
return {type: AnimationMetadataType.Group, steps, options};
|
||||
}
|
||||
|
||||
@ -721,7 +728,8 @@ export function group(
|
||||
* @usageNotes
|
||||
* When you pass an array of steps to a
|
||||
* `transition()` call, the steps run sequentially by default.
|
||||
* Compare this to the `{@link animations/group group()}` call, which runs animation steps in parallel.
|
||||
* Compare this to the `{@link animations/group group()}` call, which runs animation steps in
|
||||
*parallel.
|
||||
*
|
||||
* When a sequence is used within a `{@link animations/group group()}` or a `transition()` call,
|
||||
* execution continues to the next instruction only after each of the inner animation
|
||||
@ -729,8 +737,8 @@ export function group(
|
||||
*
|
||||
* @publicApi
|
||||
**/
|
||||
export function sequence(steps: AnimationMetadata[], options: AnimationOptions | null = null):
|
||||
AnimationSequenceMetadata {
|
||||
export function sequence(
|
||||
steps: AnimationMetadata[], options: AnimationOptions|null = null): AnimationSequenceMetadata {
|
||||
return {type: AnimationMetadataType.Sequence, steps, options};
|
||||
}
|
||||
|
||||
@ -773,9 +781,8 @@ export function sequence(steps: AnimationMetadata[], options: AnimationOptions |
|
||||
*
|
||||
* @publicApi
|
||||
**/
|
||||
export function style(
|
||||
tokens: '*' | {[key: string]: string | number} |
|
||||
Array<'*'|{[key: string]: string | number}>): AnimationStyleMetadata {
|
||||
export function style(tokens: '*'|{[key: string]: string | number}|
|
||||
Array<'*'|{[key: string]: string | number}>): AnimationStyleMetadata {
|
||||
return {type: AnimationMetadataType.Style, styles: tokens, offset: null};
|
||||
}
|
||||
|
||||
@ -1032,10 +1039,10 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
|
||||
* @publicApi
|
||||
**/
|
||||
export function transition(
|
||||
stateChangeExpr: string | ((fromState: string, toState: string, element?: any,
|
||||
params?: {[key: string]: any}) => boolean),
|
||||
steps: AnimationMetadata | AnimationMetadata[],
|
||||
options: AnimationOptions | null = null): AnimationTransitionMetadata {
|
||||
stateChangeExpr: string|
|
||||
((fromState: string, toState: string, element?: any, params?: {[key: string]: any}) => boolean),
|
||||
steps: AnimationMetadata|AnimationMetadata[],
|
||||
options: AnimationOptions|null = null): AnimationTransitionMetadata {
|
||||
return {type: AnimationMetadataType.Transition, expr: stateChangeExpr, animation: steps, options};
|
||||
}
|
||||
|
||||
@ -1085,8 +1092,8 @@ export function transition(
|
||||
* @publicApi
|
||||
*/
|
||||
export function animation(
|
||||
steps: AnimationMetadata | AnimationMetadata[],
|
||||
options: AnimationOptions | null = null): AnimationReferenceMetadata {
|
||||
steps: AnimationMetadata|AnimationMetadata[],
|
||||
options: AnimationOptions|null = null): AnimationReferenceMetadata {
|
||||
return {type: AnimationMetadataType.Reference, animation: steps, options};
|
||||
}
|
||||
|
||||
@ -1109,7 +1116,7 @@ export function animation(
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function animateChild(options: AnimateChildOptions | null = null):
|
||||
export function animateChild(options: AnimateChildOptions|null = null):
|
||||
AnimationAnimateChildMetadata {
|
||||
return {type: AnimationMetadataType.AnimateChild, options};
|
||||
}
|
||||
@ -1126,7 +1133,7 @@ export function animateChild(options: AnimateChildOptions | null = null):
|
||||
*/
|
||||
export function useAnimation(
|
||||
animation: AnimationReferenceMetadata,
|
||||
options: AnimationOptions | null = null): AnimationAnimateRefMetadata {
|
||||
options: AnimationOptions|null = null): AnimationAnimateRefMetadata {
|
||||
return {type: AnimationMetadataType.AnimateRef, animation, options};
|
||||
}
|
||||
|
||||
@ -1179,7 +1186,7 @@ export function useAnimation(
|
||||
* ### Usage Example
|
||||
*
|
||||
* The following example queries for inner elements and animates them
|
||||
* individually using `animate()`.
|
||||
* individually using `animate()`.
|
||||
*
|
||||
* ```typescript
|
||||
* @Component({
|
||||
@ -1218,8 +1225,8 @@ export function useAnimation(
|
||||
* @publicApi
|
||||
*/
|
||||
export function query(
|
||||
selector: string, animation: AnimationMetadata | AnimationMetadata[],
|
||||
options: AnimationQueryOptions | null = null): AnimationQueryMetadata {
|
||||
selector: string, animation: AnimationMetadata|AnimationMetadata[],
|
||||
options: AnimationQueryOptions|null = null): AnimationQueryMetadata {
|
||||
return {type: AnimationMetadataType.Query, selector, animation, options};
|
||||
}
|
||||
|
||||
@ -1303,8 +1310,7 @@ export function query(
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function stagger(
|
||||
timings: string | number,
|
||||
animation: AnimationMetadata | AnimationMetadata[]): AnimationStaggerMetadata {
|
||||
export function stagger(timings: string|number, animation: AnimationMetadata|AnimationMetadata[]):
|
||||
AnimationStaggerMetadata {
|
||||
return {type: AnimationMetadataType.Stagger, timings, animation};
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
*/
|
||||
export {AnimationBuilder, AnimationFactory} from './animation_builder';
|
||||
export {AnimationEvent} from './animation_event';
|
||||
export {AUTO_STYLE, AnimateChildOptions, AnimateTimings, AnimationAnimateChildMetadata, AnimationAnimateMetadata, AnimationAnimateRefMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationMetadataType, AnimationOptions, AnimationQueryMetadata, AnimationQueryOptions, AnimationReferenceMetadata, AnimationSequenceMetadata, AnimationStaggerMetadata, AnimationStateMetadata, AnimationStyleMetadata, AnimationTransitionMetadata, AnimationTriggerMetadata, animate, animateChild, animation, group, keyframes, query, sequence, stagger, state, style, transition, trigger, useAnimation, ɵStyleData} from './animation_metadata';
|
||||
export {animate, animateChild, AnimateChildOptions, AnimateTimings, animation, AnimationAnimateChildMetadata, AnimationAnimateMetadata, AnimationAnimateRefMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationMetadataType, AnimationOptions, AnimationQueryMetadata, AnimationQueryOptions, AnimationReferenceMetadata, AnimationSequenceMetadata, AnimationStaggerMetadata, AnimationStateMetadata, AnimationStyleMetadata, AnimationTransitionMetadata, AnimationTriggerMetadata, AUTO_STYLE, group, keyframes, query, sequence, stagger, state, style, transition, trigger, useAnimation, ɵStyleData} from './animation_metadata';
|
||||
export {AnimationPlayer, NoopAnimationPlayer} from './players/animation_player';
|
||||
|
||||
export * from './private_export';
|
||||
|
@ -69,9 +69,13 @@ export class AnimationGroupPlayer implements AnimationPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
init(): void { this.players.forEach(player => player.init()); }
|
||||
init(): void {
|
||||
this.players.forEach(player => player.init());
|
||||
}
|
||||
|
||||
onStart(fn: () => void): void { this._onStartFns.push(fn); }
|
||||
onStart(fn: () => void): void {
|
||||
this._onStartFns.push(fn);
|
||||
}
|
||||
|
||||
private _onStart() {
|
||||
if (!this.hasStarted()) {
|
||||
@ -81,11 +85,17 @@ export class AnimationGroupPlayer implements AnimationPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
onDone(fn: () => void): void { this._onDoneFns.push(fn); }
|
||||
onDone(fn: () => void): void {
|
||||
this._onDoneFns.push(fn);
|
||||
}
|
||||
|
||||
onDestroy(fn: () => void): void { this._onDestroyFns.push(fn); }
|
||||
onDestroy(fn: () => void): void {
|
||||
this._onDestroyFns.push(fn);
|
||||
}
|
||||
|
||||
hasStarted() { return this._started; }
|
||||
hasStarted() {
|
||||
return this._started;
|
||||
}
|
||||
|
||||
play() {
|
||||
if (!this.parentPlayer) {
|
||||
@ -95,16 +105,22 @@ export class AnimationGroupPlayer implements AnimationPlayer {
|
||||
this.players.forEach(player => player.play());
|
||||
}
|
||||
|
||||
pause(): void { this.players.forEach(player => player.pause()); }
|
||||
pause(): void {
|
||||
this.players.forEach(player => player.pause());
|
||||
}
|
||||
|
||||
restart(): void { this.players.forEach(player => player.restart()); }
|
||||
restart(): void {
|
||||
this.players.forEach(player => player.restart());
|
||||
}
|
||||
|
||||
finish(): void {
|
||||
this._onFinish();
|
||||
this.players.forEach(player => player.finish());
|
||||
}
|
||||
|
||||
destroy(): void { this._onDestroy(); }
|
||||
destroy(): void {
|
||||
this._onDestroy();
|
||||
}
|
||||
|
||||
private _onDestroy() {
|
||||
if (!this._destroyed) {
|
||||
|
@ -94,11 +94,13 @@ export interface AnimationPlayer {
|
||||
* Provides a callback to invoke before the animation is destroyed.
|
||||
*/
|
||||
beforeDestroy?: () => any;
|
||||
/** @internal
|
||||
/**
|
||||
* @internal
|
||||
* Internal
|
||||
*/
|
||||
triggerCallback?: (phaseName: string) => void;
|
||||
/** @internal
|
||||
/**
|
||||
* @internal
|
||||
* Internal
|
||||
*/
|
||||
disabled?: boolean;
|
||||
@ -124,7 +126,9 @@ export class NoopAnimationPlayer implements AnimationPlayer {
|
||||
private _finished = false;
|
||||
public parentPlayer: AnimationPlayer|null = null;
|
||||
public readonly totalTime: number;
|
||||
constructor(duration: number = 0, delay: number = 0) { this.totalTime = duration + delay; }
|
||||
constructor(duration: number = 0, delay: number = 0) {
|
||||
this.totalTime = duration + delay;
|
||||
}
|
||||
private _onFinish() {
|
||||
if (!this._finished) {
|
||||
this._finished = true;
|
||||
@ -132,10 +136,18 @@ export class NoopAnimationPlayer implements AnimationPlayer {
|
||||
this._onDoneFns = [];
|
||||
}
|
||||
}
|
||||
onStart(fn: () => void): void { this._onStartFns.push(fn); }
|
||||
onDone(fn: () => void): void { this._onDoneFns.push(fn); }
|
||||
onDestroy(fn: () => void): void { this._onDestroyFns.push(fn); }
|
||||
hasStarted(): boolean { return this._started; }
|
||||
onStart(fn: () => void): void {
|
||||
this._onStartFns.push(fn);
|
||||
}
|
||||
onDone(fn: () => void): void {
|
||||
this._onDoneFns.push(fn);
|
||||
}
|
||||
onDestroy(fn: () => void): void {
|
||||
this._onDestroyFns.push(fn);
|
||||
}
|
||||
hasStarted(): boolean {
|
||||
return this._started;
|
||||
}
|
||||
init(): void {}
|
||||
play(): void {
|
||||
if (!this.hasStarted()) {
|
||||
@ -146,7 +158,9 @@ export class NoopAnimationPlayer implements AnimationPlayer {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
triggerMicrotask() { scheduleMicroTask(() => this._onFinish()); }
|
||||
triggerMicrotask() {
|
||||
scheduleMicroTask(() => this._onFinish());
|
||||
}
|
||||
|
||||
private _onStart() {
|
||||
this._onStartFns.forEach(fn => fn());
|
||||
@ -155,7 +169,9 @@ export class NoopAnimationPlayer implements AnimationPlayer {
|
||||
|
||||
pause(): void {}
|
||||
restart(): void {}
|
||||
finish(): void { this._onFinish(); }
|
||||
finish(): void {
|
||||
this._onFinish();
|
||||
}
|
||||
destroy(): void {
|
||||
if (!this._destroyed) {
|
||||
this._destroyed = true;
|
||||
@ -169,7 +185,9 @@ export class NoopAnimationPlayer implements AnimationPlayer {
|
||||
}
|
||||
reset(): void {}
|
||||
setPosition(position: number): void {}
|
||||
getPosition(): number { return 0; }
|
||||
getPosition(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
triggerCallback(phaseName: string): void {
|
||||
|
@ -69,7 +69,9 @@ import {scheduleMicroTask} from '../src/util';
|
||||
const log: string[] = [];
|
||||
|
||||
const player = new NoopAnimationPlayer();
|
||||
player.onStart(() => { scheduleMicroTask(() => log.push('started')); });
|
||||
player.onStart(() => {
|
||||
scheduleMicroTask(() => log.push('started'));
|
||||
});
|
||||
player.onDone(() => log.push('done'));
|
||||
expect(log).toEqual([]);
|
||||
|
||||
|
Reference in New Issue
Block a user