diff --git a/core/src/utils/animation/animation.ts b/core/src/utils/animation/animation.ts
index 95b1275ef8..7ab91dd817 100644
--- a/core/src/utils/animation/animation.ts
+++ b/core/src/utils/animation/animation.ts
@@ -56,6 +56,24 @@ const generateKeyframeString = (name: string | undefined, keyframes: any[] = [])
return keyframeString.join(' ');
};
+const createKeyframeStylesheet = (keyframeString: string): HTMLElement => {
+ const stylesheet = document.createElement('style');
+ stylesheet.appendChild(document.createTextNode(keyframeString));
+ document.querySelector('head')!.appendChild(stylesheet);
+
+ return stylesheet;
+};
+
+const addClassToArray = (classes: string[] = [], className: string | string[] | undefined): string[] => {
+ if (className !== undefined) {
+ const classNameToAppend = (Array.isArray(className)) ? className : [className];
+
+ return [...classes, ...classNameToAppend];
+ }
+
+ return classes;
+};
+
export class Animation {
private elements: Element[] = [];
private childAnimations: Animation[] = [];
@@ -74,32 +92,138 @@ export class Animation {
parentAnimation: Animation | undefined;
+ private beforeAddClasses: string[] = [];
+ private beforeRemoveClasses: string[] = [];
+ private beforeStylesValue: { [property: string]: any } = {};
+
+ private afterAddClasses: string[] = [];
+ private afterRemoveClasses: string[] = [];
+ private afterStylesValue: { [property: string]: any } = {};
+
constructor(public _name: string | undefined) {}
+ /**
+ * Destroy this animation and all child animations.
+ */
+ destroy(): Animation {
+ this.childAnimations.forEach(childAnimation => {
+ childAnimation.destroy();
+ });
+
+ this.elements = [];
+ this.childAnimations = [];
+
+ return this;
+ }
+
+ /**
+ * Add CSS class to this animation's elements
+ * before the animation begins.
+ */
+ beforeAddClass(className: string | string[] | undefined): Animation {
+ this.beforeAddClasses = addClassToArray(this.beforeAddClasses, className);
+
+ return this;
+ }
+
+ /**
+ * Remove CSS class from this animation's elements
+ * before the animation begins.
+ */
+ beforeRemoveClass(className: string | string[] | undefined): Animation {
+ this.beforeRemoveClasses = addClassToArray(this.beforeRemoveClasses, className);
+
+ return this;
+ }
+
+ /**
+ * Set CSS inline styles to this animation's elements
+ * before the animation begins.
+ */
+ beforeStyles(styles: { [property: string]: any } = {}): Animation {
+ this.beforeStylesValue = styles;
+
+ return this;
+ }
+
+ /**
+ * Clear CSS inline styles from this animation's elements
+ * before the animation begins.
+ */
+ beforeClearStyles(propertyNames: string[] = []): Animation {
+ for (const property of propertyNames) {
+ this.beforeStylesValue[property] = '';
+ }
+
+ return this;
+ }
+
+ /**
+ * Add CSS class to this animation's elements
+ * after the animation ends.
+ */
+ afterAddClass(className: string | string[] | undefined): Animation {
+ this.afterAddClasses = addClassToArray(this.afterAddClasses, className);
+
+ return this;
+ }
+
+ /**
+ * Remove CSS class from this animation's elements
+ * after the animation ends.
+ */
+ afterRemoveClass(className: string | string[] | undefined): Animation {
+ this.afterRemoveClasses = addClassToArray(this.afterRemoveClasses, className);
+
+ return this;
+ }
+
+ /**
+ * Set CSS inline styles to this animation's elements
+ * after the animation ends.
+ */
+ afterStyles(styles: { [property: string]: any } = {}): Animation {
+ this.afterStylesValue = styles;
+
+ return this;
+ }
+
+ /**
+ * Clear CSS inline styles from this animation's elements
+ * after the animation ends.
+ */
+ afterClearStyles(propertyNames: string[] = []): Animation {
+ for (const property of propertyNames) {
+ this.afterStylesValue[property] = '';
+ }
+
+ return this;
+ }
+
getEasing(): string | undefined {
if (this._easing !== undefined) { return this._easing; }
- if (this.parentAnimation && this.parentAnimation.getEasing() !== undefined) { return this.parentAnimation.getEasing(); }
+ if (this.parentAnimation) { return this.parentAnimation.getEasing(); }
return undefined;
}
getDuration(): number | undefined {
if (this._duration !== undefined) { return this._duration; }
- if (this.parentAnimation && this.parentAnimation.getDuration() !== undefined) { return this.parentAnimation.getDuration(); }
+ if (this.parentAnimation) { return this.parentAnimation.getDuration(); }
return undefined;
}
getIterations(): number | undefined {
if (this._iterations !== undefined) { return this._iterations; }
- if (this.parentAnimation && this.parentAnimation.getIterations() !== undefined) { return this.parentAnimation.getIterations(); }
+ if (this.parentAnimation) { return this.parentAnimation.getIterations(); }
return undefined;
}
getDelay(): number | undefined {
if (this._delay !== undefined) { return this._delay; }
- if (this.parentAnimation && this.parentAnimation.getDelay() !== undefined) { return this.parentAnimation.getDelay(); }
+ if (this.parentAnimation) { return this.parentAnimation.getDelay(); }
return undefined;
}
@@ -165,12 +289,7 @@ export class Animation {
private initializeAnimation(): void {
if (!this.stylesheet) {
- const stylesheet = document.createElement('style');
- stylesheet.appendChild(document.createTextNode(this._keyframeString));
-
- document.querySelector('head')!.appendChild(stylesheet);
-
- this.stylesheet = stylesheet;
+ this.stylesheet = createKeyframeStylesheet(this._keyframeString);
}
const animationName = this._name;
@@ -227,18 +346,16 @@ export class Animation {
}
pause(): Animation {
- if (!this.initialized) {
- this.initializeAnimation();
+ if (this.initialized) {
+ this.childAnimations.forEach(animation => {
+ animation.pause();
+ });
+
+ this.elements.forEach(element => {
+ (element as HTMLElement).style.animationPlayState = 'paused';
+ });
}
- this.childAnimations.forEach(animation => {
- animation.pause();
- });
-
- this.elements.forEach(element => {
- (element as HTMLElement).style.animationPlayState = 'paused';
- });
-
return this;
}
diff --git a/core/src/utils/animation/test/animation.spec.ts b/core/src/utils/animation/test/animation.spec.ts
index 57e9c740d7..4f6e1929ad 100644
--- a/core/src/utils/animation/test/animation.spec.ts
+++ b/core/src/utils/animation/test/animation.spec.ts
@@ -123,6 +123,83 @@ describe('Animation Class', () => {
});
});
+ describe('Before and After Animation Methods', () => {
+ let animation;
+ beforeEach(() => {
+ animation = new Animation();
+ });
+
+ it('should register all "before" styles', () => {
+ animation.beforeStyles({ 'background': 'red', 'opacity': 1 });
+ expect(Object.keys(animation.beforeStylesValue).length).toEqual(2);
+ });
+
+ it('should register all "before" classes given arrays', () => {
+ const classesToAdd = ['my-class', 'hello-world'];
+ const classesToRemove = ['ionic-framework'];
+
+ animation.beforeAddClass(classesToAdd);
+ animation.beforeRemoveClass(classesToRemove);
+
+ expect(animation.beforeAddClasses.length).toEqual(classesToAdd.length);
+ expect(animation.beforeRemoveClasses.length).toEqual(classesToRemove.length);
+ });
+
+ it('should register all "before" classes given strings', () => {
+ const classesToAdd = 'my-class';
+ const classesToRemove = 'ionic-framework';
+
+ animation.beforeAddClass(classesToAdd);
+ animation.beforeRemoveClass(classesToRemove);
+
+ expect(animation.beforeAddClasses.length).toEqual(1);
+ expect(animation.beforeRemoveClasses.length).toEqual(1);
+ });
+
+ it('should not register "before" classes given undefined', () => {
+ animation.beforeAddClass(undefined);
+ animation.beforeRemoveClass(undefined);
+
+ expect(animation.beforeAddClasses.length).toEqual(0);
+ expect(animation.beforeRemoveClasses.length).toEqual(0);
+ });
+
+ it('should register all "after" styles', () => {
+ animation.afterStyles({ 'background': 'red', 'opacity': 1 });
+ expect(Object.keys(animation.afterStylesValue).length).toEqual(2);
+ });
+
+ it('should register all "after" classes given arrays', () => {
+ const classesToAdd = ['my-class', 'hello-world'];
+ const classesToRemove = ['ionic-framework'];
+
+ animation.afterAddClass(classesToAdd);
+ animation.afterRemoveClass(classesToRemove);
+
+ expect(animation.afterAddClasses.length).toEqual(classesToAdd.length);
+ expect(animation.afterRemoveClasses.length).toEqual(classesToRemove.length);
+ });
+
+ it('should register all "after" classes given strings', () => {
+ const classesToAdd = 'my-class';
+ const classesToRemove = 'ionic-framework';
+
+ animation.afterAddClass(classesToAdd);
+ animation.afterRemoveClass(classesToRemove);
+
+ expect(animation.afterAddClasses.length).toEqual(1);
+ expect(animation.afterRemoveClasses.length).toEqual(1);
+ });
+
+ it('should not register "after" classes given undefined', () => {
+ animation.afterAddClass(undefined);
+ animation.afterRemoveClass(undefined);
+
+ expect(animation.afterAddClasses.length).toEqual(0);
+ expect(animation.afterRemoveClasses.length).toEqual(0);
+ });
+ });
+
describe('Animation Config Methods', () => {
let animation;
beforeEach(() => {
diff --git a/core/src/utils/animation/test/basic/index.html b/core/src/utils/animation/test/basic/index.html
index 6c3de30a57..032d5ae9b2 100644
--- a/core/src/utils/animation/test/basic/index.html
+++ b/core/src/utils/animation/test/basic/index.html
@@ -13,7 +13,7 @@