refactor(animation): update animation API (#19529)

This commit is contained in:
Manu MA
2019-10-02 19:52:46 +02:00
committed by GitHub
parent 71b8853ff4
commit f4818a1f3a
35 changed files with 483 additions and 439 deletions

View File

@ -10,11 +10,11 @@ export const iosEnterAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.01, 0.4); .fromTo('opacity', 0.01, 0.4);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.action-sheet-wrapper')) .addElement(baseEl.querySelector('.action-sheet-wrapper')!)
.fromTo('transform', 'translateY(100%)', 'translateY(0%)'); .fromTo('transform', 'translateY(100%)', 'translateY(0%)');
return baseAnimation return baseAnimation

View File

@ -10,11 +10,11 @@ export const iosLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.4, 0); .fromTo('opacity', 0.4, 0);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.action-sheet-wrapper')) .addElement(baseEl.querySelector('.action-sheet-wrapper')!)
.fromTo('transform', 'translateY(0%)', 'translateY(100%)'); .fromTo('transform', 'translateY(0%)', 'translateY(100%)');
return baseAnimation return baseAnimation

View File

@ -10,11 +10,11 @@ export const mdEnterAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.01, 0.32); .fromTo('opacity', 0.01, 0.32);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.action-sheet-wrapper')) .addElement(baseEl.querySelector('.action-sheet-wrapper')!)
.fromTo('transform', 'translateY(100%)', 'translateY(0%)'); .fromTo('transform', 'translateY(100%)', 'translateY(0%)');
return baseAnimation return baseAnimation

View File

@ -10,11 +10,11 @@ export const mdLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.32, 0); .fromTo('opacity', 0.32, 0);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.action-sheet-wrapper')) .addElement(baseEl.querySelector('.action-sheet-wrapper')!)
.fromTo('transform', 'translateY(0%)', 'translateY(100%)'); .fromTo('transform', 'translateY(0%)', 'translateY(100%)');
return baseAnimation return baseAnimation

View File

@ -10,14 +10,14 @@ export const iosEnterAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.01, 0.3); .fromTo('opacity', 0.01, 0.3);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.alert-wrapper')) .addElement(baseEl.querySelector('.alert-wrapper')!)
.keyframes([ .keyframes([
{ offset: 0, opacity: 0.01, transform: 'scale(1.1)' }, { offset: 0, opacity: '0.01', transform: 'scale(1.1)' },
{ offset: 1, opacity: 1, transform: 'scale(1)' } { offset: 1, opacity: '1', transform: 'scale(1)' }
]); ]);
return baseAnimation return baseAnimation

View File

@ -10,11 +10,11 @@ export const iosLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.3, 0); .fromTo('opacity', 0.3, 0);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.alert-wrapper')) .addElement(baseEl.querySelector('.alert-wrapper')!)
.keyframes([ .keyframes([
{ offset: 0, opacity: 0.99, transform: 'scale(1)' }, { offset: 0, opacity: 0.99, transform: 'scale(1)' },
{ offset: 1, opacity: 0, transform: 'scale(0.9)' } { offset: 1, opacity: 0, transform: 'scale(0.9)' }

View File

@ -10,14 +10,14 @@ export const mdEnterAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.01, 0.32); .fromTo('opacity', 0.01, 0.32);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.alert-wrapper')) .addElement(baseEl.querySelector('.alert-wrapper')!)
.keyframes([ .keyframes([
{ offset: 0, opacity: 0.01, transform: 'scale(0.9)' }, { offset: 0, opacity: '0.01', transform: 'scale(0.9)' },
{ offset: 1, opacity: 1, transform: 'scale(1)' } { offset: 1, opacity: '1', transform: 'scale(1)' }
]); ]);
return baseAnimation return baseAnimation

View File

@ -10,11 +10,11 @@ export const mdLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.32, 0); .fromTo('opacity', 0.32, 0);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.alert-wrapper')) .addElement(baseEl.querySelector('.alert-wrapper')!)
.fromTo('opacity', 0.99, 0); .fromTo('opacity', 0.99, 0);
return baseAnimation return baseAnimation

View File

@ -10,11 +10,11 @@ export const iosEnterAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.01, 0.3); .fromTo('opacity', 0.01, 0.3);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.loading-wrapper')) .addElement(baseEl.querySelector('.loading-wrapper')!)
.keyframes([ .keyframes([
{ offset: 0, opacity: 0.01, transform: 'scale(1.1)' }, { offset: 0, opacity: 0.01, transform: 'scale(1.1)' },
{ offset: 1, opacity: 1, transform: 'scale(1)' } { offset: 1, opacity: 1, transform: 'scale(1)' }

View File

@ -10,11 +10,11 @@ export const iosLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.3, 0); .fromTo('opacity', 0.3, 0);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.loading-wrapper')) .addElement(baseEl.querySelector('.loading-wrapper')!)
.keyframes([ .keyframes([
{ offset: 0, opacity: 0.99, transform: 'scale(1)' }, { offset: 0, opacity: 0.99, transform: 'scale(1)' },
{ offset: 1, opacity: 0, transform: 'scale(0.9)' } { offset: 1, opacity: 0, transform: 'scale(0.9)' }

View File

@ -10,11 +10,11 @@ export const mdEnterAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.01, 0.32); .fromTo('opacity', 0.01, 0.32);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.loading-wrapper')) .addElement(baseEl.querySelector('.loading-wrapper')!)
.keyframes([ .keyframes([
{ offset: 0, opacity: 0.01, transform: 'scale(1.1)' }, { offset: 0, opacity: 0.01, transform: 'scale(1.1)' },
{ offset: 1, opacity: 1, transform: 'scale(1)' } { offset: 1, opacity: 1, transform: 'scale(1)' }

View File

@ -10,11 +10,11 @@ export const mdLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.32, 0); .fromTo('opacity', 0.32, 0);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.loading-wrapper')) .addElement(baseEl.querySelector('.loading-wrapper')!)
.keyframes([ .keyframes([
{ offset: 0, opacity: 0.99, transform: 'scale(1)' }, { offset: 0, opacity: 0.99, transform: 'scale(1)' },
{ offset: 1, opacity: 0, transform: 'scale(0.9)' } { offset: 1, opacity: 0, transform: 'scale(0.9)' }

View File

@ -150,6 +150,6 @@ export class MenuController {
*/ */
@Method() @Method()
async registerAnimation(name: string, animation: AnimationBuilder | ((menu: MenuI) => IonicAnimation)) { async registerAnimation(name: string, animation: AnimationBuilder | ((menu: MenuI) => IonicAnimation)) {
return menuController.registerAnimation(name, animation); return menuController.registerAnimation(name, animation as any);
} }
} }

View File

@ -434,10 +434,10 @@ export class Menu implements ComponentInterface, MenuI {
this.animation this.animation
.easing('cubic-bezier(0.4, 0.0, 0.6, 1)') .easing('cubic-bezier(0.4, 0.0, 0.6, 1)')
.onFinish(() => this.afterAnimation(shouldOpen), { .onFinish(
oneTimeCallback: true () => this.afterAnimation(shouldOpen),
}) { oneTimeCallback: true })
.progressEnd(shouldComplete, newStepValue, 300); .progressEnd(shouldComplete ? 1 : 0, newStepValue, 300);
} }
private beforeAnimation(shouldOpen: boolean) { private beforeAnimation(shouldOpen: boolean) {

View File

@ -10,11 +10,11 @@ export const iosEnterAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.01, 0.4); .fromTo('opacity', 0.01, 0.4);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.modal-wrapper')) .addElement(baseEl.querySelector('.modal-wrapper')!)
.beforeStyles({ 'opacity': 1 }) .beforeStyles({ 'opacity': 1 })
.fromTo('transform', 'translateY(100%)', 'translateY(0%)'); .fromTo('transform', 'translateY(100%)', 'translateY(0%)');

View File

@ -12,11 +12,11 @@ export const iosLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperElRect = wrapperEl!.getBoundingClientRect(); const wrapperElRect = wrapperEl!.getBoundingClientRect();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.4, 0.0); .fromTo('opacity', 0.4, 0.0);
wrapperAnimation wrapperAnimation
.addElement(wrapperEl) .addElement(wrapperEl!)
.beforeStyles({ 'opacity': 1 }) .beforeStyles({ 'opacity': 1 })
.fromTo('transform', 'translateY(0%)', `translateY(${(baseEl.ownerDocument as any).defaultView.innerHeight - wrapperElRect.top}px)`); .fromTo('transform', 'translateY(0%)', `translateY(${(baseEl.ownerDocument as any).defaultView.innerHeight - wrapperElRect.top}px)`);

View File

@ -10,11 +10,11 @@ export const mdEnterAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.01, 0.32); .fromTo('opacity', 0.01, 0.32);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.modal-wrapper')) .addElement(baseEl.querySelector('.modal-wrapper')!)
.keyframes([ .keyframes([
{ offset: 0, opacity: 0.01, transform: 'translateY(40px)' }, { offset: 0, opacity: 0.01, transform: 'translateY(40px)' },
{ offset: 1, opacity: 1, transform: 'translateY(0px)' } { offset: 1, opacity: 1, transform: 'translateY(0px)' }

View File

@ -8,10 +8,10 @@ export const mdLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const baseAnimation = createAnimation(); const baseAnimation = createAnimation();
const backdropAnimation = createAnimation(); const backdropAnimation = createAnimation();
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
const wrapperEl = baseEl.querySelector('.modal-wrapper'); const wrapperEl = baseEl.querySelector('.modal-wrapper')!;
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.32, 0.0); .fromTo('opacity', 0.32, 0.0);
wrapperAnimation wrapperAnimation

View File

@ -986,7 +986,7 @@ export class Nav implements NavOutlet {
newStepValue += getTimeGivenProgression(new Point(0, 0), new Point(0.32, 0.72), new Point(0, 1), new Point(1, 1), stepValue); newStepValue += getTimeGivenProgression(new Point(0, 0), new Point(0.32, 0.72), new Point(0, 1), new Point(1, 1), stepValue);
} }
this.sbAni.progressEnd(shouldComplete, newStepValue, dur); (this.sbAni as IonicAnimation).progressEnd(shouldComplete ? 1 : 0, newStepValue, dur);
} }
} }

View File

@ -10,11 +10,11 @@ export const iosEnterAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.01, 0.26); .fromTo('opacity', 0.01, 0.26);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.picker-wrapper')) .addElement(baseEl.querySelector('.picker-wrapper')!)
.fromTo('transform', 'translateY(100%)', 'translateY(0%)'); .fromTo('transform', 'translateY(100%)', 'translateY(0%)');
return baseAnimation return baseAnimation

View File

@ -10,11 +10,11 @@ export const iosLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.26, 0.01); .fromTo('opacity', 0.26, 0.01);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.picker-wrapper')) .addElement(baseEl.querySelector('.picker-wrapper')!)
.fromTo('transform', 'translateY(0%)', 'translateY(100%)'); .fromTo('transform', 'translateY(0%)', 'translateY(100%)');
return baseAnimation return baseAnimation

View File

@ -103,11 +103,11 @@ export const iosEnterAnimation = (baseEl: HTMLElement, ev?: Event): IonicAnimati
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.01, 0.08); .fromTo('opacity', 0.01, 0.08);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.popover-wrapper')) .addElement(baseEl.querySelector('.popover-wrapper')!)
.fromTo('opacity', 0.01, 1); .fromTo('opacity', 0.01, 1);
return baseAnimation return baseAnimation

View File

@ -10,11 +10,11 @@ export const iosLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.08, 0); .fromTo('opacity', 0.08, 0);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.popover-wrapper')) .addElement(baseEl.querySelector('.popover-wrapper')!)
.fromTo('opacity', 0.99, 0); .fromTo('opacity', 0.99, 0);
return baseAnimation return baseAnimation

View File

@ -85,11 +85,11 @@ export const mdEnterAnimation = (baseEl: HTMLElement, ev?: Event): IonicAnimatio
const viewportAnimation = createAnimation(); const viewportAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.01, 0.32); .fromTo('opacity', 0.01, 0.32);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.popover-wrapper')) .addElement(baseEl.querySelector('.popover-wrapper')!)
.fromTo('opacity', 0.01, 1); .fromTo('opacity', 0.01, 1);
contentAnimation contentAnimation
@ -102,7 +102,7 @@ export const mdEnterAnimation = (baseEl: HTMLElement, ev?: Event): IonicAnimatio
.fromTo('transform', 'scale(0.001)', 'scale(1)'); .fromTo('transform', 'scale(0.001)', 'scale(1)');
viewportAnimation viewportAnimation
.addElement(baseEl.querySelector('.popover-viewport')) .addElement(baseEl.querySelector('.popover-viewport')!)
.fromTo('opacity', 0.01, 1); .fromTo('opacity', 0.01, 1);
return baseAnimation return baseAnimation

View File

@ -10,11 +10,11 @@ export const mdLeaveAnimation = (baseEl: HTMLElement): IonicAnimation => {
const wrapperAnimation = createAnimation(); const wrapperAnimation = createAnimation();
backdropAnimation backdropAnimation
.addElement(baseEl.querySelector('ion-backdrop')) .addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', 0.32, 0); .fromTo('opacity', 0.32, 0);
wrapperAnimation wrapperAnimation
.addElement(baseEl.querySelector('.popover-wrapper')) .addElement(baseEl.querySelector('.popover-wrapper')!)
.fromTo('opacity', 0.99, 0); .fromTo('opacity', 0.99, 0);
return baseAnimation return baseAnimation

View File

@ -96,7 +96,7 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
newStepValue += getTimeGivenProgression(new Point(0, 0), new Point(0.32, 0.72), new Point(0, 1), new Point(1, 1), step); newStepValue += getTimeGivenProgression(new Point(0, 0), new Point(0.32, 0.72), new Point(0, 1), new Point(1, 1), step);
} }
this.ani.progressEnd(shouldComplete, newStepValue, dur); (this.ani as IonicAnimation).progressEnd(shouldComplete ? 1 : 0, newStepValue, dur);
} }
} }

View File

@ -3,69 +3,239 @@ export interface Animation {
elements: HTMLElement[]; elements: HTMLElement[];
childAnimations: Animation[]; childAnimations: Animation[];
animationFinish(): void; /**
play(): Animation; * Play the animation
playAsync(): Promise<Animation>; *
playSync(): Animation; * If the `sync` options is `true`, the animation will play synchronously. This
pause(): Animation; * is the equivalent of running the animation
stop(): Animation; * with a duration of 0ms.
destroy(): Animation; */
play(opts?: AnimationPlayOptions): Promise<void>;
progressStart(forceLinearEasing: boolean): Animation; /**
progressStep(step: number): Animation; * Pauses the animation
progressEnd(shouldComplete: boolean, step: number, dur: number | undefined): Animation; */
pause(): void;
/**
* Stop the animation and reset
* all elements to their initial state
*/
stop(): void;
/**
* Destroy the animation and all child animations.
*/
destroy(): void;
progressStart(forceLinearEasing: boolean): void;
progressStep(step: number): void;
progressEnd(playTo: 0 | 1 | undefined, step: number, dur?: number): void;
from(property: string, value: any): Animation; from(property: string, value: any): Animation;
to(property: string, value: any): Animation; to(property: string, value: any): Animation;
fromTo(property: string, fromValue: any, toValue: any): Animation; fromTo(property: string, fromValue: any, toValue: any): Animation;
keyframes(keyframes: any[]): Animation;
addAnimation(animationToADd: Animation | Animation[] | undefined | null): Animation; /**
addElement(el: Element | Element[] | Node | Node[] | NodeList | undefined | null): Animation; * Set the keyframes for the animation.
*/
keyframes(keyframes: AnimationKeyFrames): Animation;
/**
* Group one or more animations together to be controlled by a parent animation.
*/
addAnimation(animationToAdd: Animation | Animation[]): Animation;
/**
* Add one or more elements to the animation
*/
addElement(el: Element | Element[] | Node | Node[] | NodeList): Animation;
/**
* Sets the number of times the animation cycle
* should be played before stopping.
*/
iterations(iterations: number): Animation; iterations(iterations: number): Animation;
fill(fill: AnimationFill | undefined): Animation;
direction(direction: AnimationDirection | undefined): Animation;
duration(duration: number): Animation;
easing(easing: string): Animation;
delay(delay: number): Animation;
parent(animation: Animation): Animation;
update(deep: boolean): Animation;
getKeyframes(): any[]; /**
getDirection(): AnimationDirection | undefined; * Sets how the animation applies styles to its
getFill(): AnimationFill | undefined; * elements before and after the animation's execution.
getDelay(): number | undefined; */
getIterations(): number | undefined; fill(fill: AnimationFill | undefined): Animation;
getEasing(): string | undefined;
getDuration(): number | undefined; /**
* Sets whether the animation should play forwards,
* backwards, or alternating back and forth.
*/
direction(direction: AnimationDirection | undefined): Animation;
/**
* Sets the length of time the animation takes
* to complete one cycle.
*/
duration(duration: number | undefined): Animation;
/**
* Sets how the animation progresses through the
* duration of each cycle.
*/
easing(easing: string | undefined): Animation;
/**
* Sets when an animation starts (in milliseconds).
*/
delay(delay: number | undefined): Animation;
/**
* Get an array of keyframes for the animation.
*/
getKeyframes(): AnimationKeyFrames;
/**
* Returns the animation's direction.
*/
getDirection(): AnimationDirection;
/**
* Returns the animation's fill mode.
*/
getFill(): AnimationFill;
/**
* Gets the animation's delay in milliseconds.
*/
getDelay(): number;
/**
* Gets the number of iterations the animation will run.
*/
getIterations(): number;
/**
* Returns the animation's easing.
*/
getEasing(): string;
/**
* Gets the animation's duration in milliseconds.
*/
getDuration(): number;
/**
* Returns the raw Web Animations object
* for all elements in an Animation.
* This will return an empty array on
* browsers that do not support
* the Web Animations API.
*/
getWebAnimations(): any[]; getWebAnimations(): any[];
/**
* Add a function that performs a
* DOM read to be run after the
* animation end
*/
afterAddRead(readFn: () => void): Animation; afterAddRead(readFn: () => void): Animation;
/**
* Add a function that performs a
* DOM write to be run after the
* animation end
*/
afterAddWrite(writeFn: () => void): Animation; afterAddWrite(writeFn: () => void): Animation;
/**
* Clear CSS inline styles from the animation's
* elements after the animation ends.
*/
afterClearStyles(propertyNames: string[]): Animation; afterClearStyles(propertyNames: string[]): Animation;
/**
* Set CSS inline styles to the animation's
* elements after the animation ends.
*/
afterStyles(styles: { [property: string]: any }): Animation; afterStyles(styles: { [property: string]: any }): Animation;
afterRemoveClass(className: string | string[] | undefined): Animation;
afterAddClass(className: string | string[] | undefined): Animation;
/**
* Add CSS class to the animation's
* elements after the animation ends.
*/
afterAddClass(className: string | string[]): Animation;
/**
* Remove CSS class from the animation's
* elements after the animation ends.
*/
afterRemoveClass(className: string | string[]): Animation;
/**
* Add a function that performs a
* DOM read to be run before the
* animation starts
*/
beforeAddRead(readFn: () => void): Animation; beforeAddRead(readFn: () => void): Animation;
/**
* Add a function that performs a
* DOM write to be run before the
* animation starts
*/
beforeAddWrite(writeFn: () => void): Animation; beforeAddWrite(writeFn: () => void): Animation;
/**
* Clear CSS inline styles from the animation's
* elements before the animation begins.
*/
beforeClearStyles(propertyNames: string[]): Animation; beforeClearStyles(propertyNames: string[]): Animation;
/**
* Set CSS inline styles to the animation's
* elements before the animation begins.
*/
beforeStyles(styles: { [property: string]: any }): Animation; beforeStyles(styles: { [property: string]: any }): Animation;
beforeRemoveClass(className: string | string[] | undefined): Animation;
beforeAddClass(className: string | string[] | undefined): Animation;
onFinish(callback: (didComplete: boolean, animation: Animation) => void, opts?: AnimationOnFinishOptions): Animation; /**
clearOnFinish(): Animation; * Add a class to the animation's
* elements before the animation starts
*/
beforeAddClass(className: string | string[]): Animation;
/**
* Remove a class from the animation's
* elements before the animation starts
*/
beforeRemoveClass(className: string | string[]): Animation;
/**
* Add a callback to be run
* upon the animation ending
*/
onFinish(callback: AnimationLifecycle, opts?: AnimationOnFinishOptions): Animation;
/** @deprecated */
playAsync(): Promise<void>;
/** @deprecated */
playSync(): void;
} }
export interface AnimationOnFinishCallback { export type AnimationLifecycle = (currentStep: 0 | 1, animation: Animation) => void;
callback: (didComplete: boolean, animation: Animation) => void;
opts: AnimationOnFinishOptions;
}
export interface AnimationOnFinishOptions { export interface AnimationOnFinishOptions {
oneTimeCallback: boolean; oneTimeCallback: boolean;
} }
export type AnimationKeyFrames = AnimationKeyFrame[];
export interface AnimationKeyFrame extends AnimationStyles {
offset: number;
}
export type AnimationStyles = Record<string, any>;
export interface AnimationPlayOptions {
sync: boolean;
}
export type AnimationPlayTo = 'start' | 'end';
export type AnimationDirection = 'normal' | 'reverse' | 'alternate' | 'alternate-reverse'; export type AnimationDirection = 'normal' | 'reverse' | 'alternate' | 'alternate-reverse';
export type AnimationFill = 'auto' | 'none' | 'forwards' | 'backwards' | 'both'; export type AnimationFill = 'auto' | 'none' | 'forwards' | 'backwards' | 'both';

View File

@ -2,21 +2,40 @@
import { raf } from '../helpers'; import { raf } from '../helpers';
import { Animation, AnimationDirection, AnimationFill, AnimationOnFinishCallback, AnimationOnFinishOptions } from './animation-interface'; import { Animation, AnimationDirection, AnimationFill, AnimationKeyFrame, AnimationKeyFrames, AnimationLifecycle, AnimationOnFinishOptions, AnimationPlayOptions } from './animation-interface';
import { addClassToArray, animationEnd, createKeyframeStylesheet, generateKeyframeName, generateKeyframeRules, removeStyleProperty, setStyleProperty } from './animation-utils'; import { addClassToArray, animationEnd, createKeyframeStylesheet, generateKeyframeName, generateKeyframeRules, removeStyleProperty, setStyleProperty } from './animation-utils';
export const createAnimation = () => { interface AnimationOnFinishCallback {
c: AnimationLifecycle;
o?: AnimationOnFinishOptions;
}
interface AnimationInternal extends Animation {
/**
* Sets the parent animation.
*/
parent(animation: Animation): Animation;
/**
* Updates any existing animations.
*/
update(deep: boolean): Animation;
animationFinish(): void;
}
export const createAnimation = (): Animation => {
let _delay: number | undefined; let _delay: number | undefined;
let _duration: number | undefined; let _duration: number | undefined;
let _easing: string | undefined; let _easing: string | undefined;
let _iterations: number | undefined; let _iterations: number | undefined;
let _fill: AnimationFill | undefined; let _fill: AnimationFill | undefined;
let _direction: AnimationDirection | undefined; let _direction: AnimationDirection | undefined;
let _keyframes: any[] = []; let _keyframes: AnimationKeyFrames = [];
let beforeAddClasses: string[] = []; let beforeAddClasses: string[] = [];
let beforeRemoveClasses: string[] = []; let beforeRemoveClasses: string[] = [];
let initialized = false; let initialized = false;
let parentAnimation: Animation | undefined; let parentAnimation: AnimationInternal | undefined;
let beforeStylesValue: { [property: string]: any } = {}; let beforeStylesValue: { [property: string]: any } = {};
let afterAddClasses: string[] = []; let afterAddClasses: string[] = [];
let afterRemoveClasses: string[] = []; let afterRemoveClasses: string[] = [];
@ -32,12 +51,12 @@ export const createAnimation = () => {
let finished = false; let finished = false;
let shouldCalculateNumAnimations = true; let shouldCalculateNumAnimations = true;
let keyframeName: string | undefined; let keyframeName: string | undefined;
let ani: Animation; let ani: AnimationInternal;
const onFinishCallbacks: AnimationOnFinishCallback[] = []; const onFinishCallbacks: AnimationOnFinishCallback[] = [];
const onFinishOneTimeCallbacks: AnimationOnFinishCallback[] = []; const onFinishOneTimeCallbacks: AnimationOnFinishCallback[] = [];
const elements: HTMLElement[] = []; const elements: HTMLElement[] = [];
const childAnimations: Animation[] = []; const childAnimations: AnimationInternal[] = [];
const stylesheets: HTMLElement[] = []; const stylesheets: HTMLElement[] = [];
const _beforeAddReadFunctions: any[] = []; const _beforeAddReadFunctions: any[] = [];
const _beforeAddWriteFunctions: any[] = []; const _beforeAddWriteFunctions: any[] = [];
@ -48,20 +67,10 @@ export const createAnimation = () => {
const supportsWebAnimations = (typeof (Element as any) === 'function') && (typeof (Element as any).prototype!.animate === 'function') && supportsAnimationEffect; const supportsWebAnimations = (typeof (Element as any) === 'function') && (typeof (Element as any).prototype!.animate === 'function') && supportsAnimationEffect;
const ANIMATION_END_FALLBACK_PADDING_MS = 100; const ANIMATION_END_FALLBACK_PADDING_MS = 100;
/**
* Returns the raw Web Animations object
* for all elements in an Animation.
* This will return an empty array on
* browsers that do not support
* the Web Animations API.
*/
const getWebAnimations = () => { const getWebAnimations = () => {
return webAnimations; return webAnimations;
}; };
/**
* Destroy the animation and all child animations.
*/
const destroy = () => { const destroy = () => {
childAnimations.forEach(childAnimation => { childAnimations.forEach(childAnimation => {
childAnimation.destroy(); childAnimation.destroy();
@ -92,20 +101,13 @@ export const createAnimation = () => {
cleanUpStyleSheets(); cleanUpStyleSheets();
}; };
/** const onFinish = (callback: AnimationLifecycle, opts?: AnimationOnFinishOptions) => {
* Add a callback to be run
* upon the animation ending
*/
const onFinish = (callback: any, opts?: AnimationOnFinishOptions) => {
const callbacks = (opts && opts.oneTimeCallback) ? onFinishOneTimeCallbacks : onFinishCallbacks; const callbacks = (opts && opts.oneTimeCallback) ? onFinishOneTimeCallbacks : onFinishCallbacks;
callbacks.push({ callback, opts } as AnimationOnFinishCallback); callbacks.push({ c: callback, o: opts });
return ani; return ani;
}; };
/**
* Clears all callbacks
*/
const clearOnFinish = () => { const clearOnFinish = () => {
onFinishCallbacks.length = 0; onFinishCallbacks.length = 0;
onFinishOneTimeCallbacks.length = 0; onFinishOneTimeCallbacks.length = 0;
@ -120,7 +122,7 @@ export const createAnimation = () => {
*/ */
const cleanUpElements = () => { const cleanUpElements = () => {
if (supportsWebAnimations) { if (supportsWebAnimations) {
getWebAnimations().forEach(animation => { webAnimations.forEach(animation => {
animation.cancel(); animation.cancel();
}); });
@ -160,64 +162,36 @@ export const createAnimation = () => {
stylesheets.length = 0; stylesheets.length = 0;
}; };
/**
* Add a function that performs a
* DOM read to be run before the
* animation starts
*/
const beforeAddRead = (readFn: () => void) => { const beforeAddRead = (readFn: () => void) => {
_beforeAddReadFunctions.push(readFn); _beforeAddReadFunctions.push(readFn);
return ani; return ani;
}; };
/**
* Add a function that performs a
* DOM write to be run before the
* animation starts
*/
const beforeAddWrite = (writeFn: () => void) => { const beforeAddWrite = (writeFn: () => void) => {
_beforeAddWriteFunctions.push(writeFn); _beforeAddWriteFunctions.push(writeFn);
return ani; return ani;
}; };
/**
* Add a function that performs a
* DOM read to be run after the
* animation end
*/
const afterAddRead = (readFn: () => void) => { const afterAddRead = (readFn: () => void) => {
_afterAddReadFunctions.push(readFn); _afterAddReadFunctions.push(readFn);
return ani; return ani;
}; };
/**
* Add a function that performs a
* DOM write to be run after the
* animation end
*/
const afterAddWrite = (writeFn: () => void) => { const afterAddWrite = (writeFn: () => void) => {
_afterAddWriteFunctions.push(writeFn); _afterAddWriteFunctions.push(writeFn);
return ani; return ani;
}; };
/**
* Add a class to the animation's
* elements before the animation starts
*/
const beforeAddClass = (className: string | string[] | undefined) => { const beforeAddClass = (className: string | string[] | undefined) => {
beforeAddClasses = addClassToArray(beforeAddClasses, className); beforeAddClasses = addClassToArray(beforeAddClasses, className);
return ani; return ani;
}; };
/**
* Remove a class from the animation's
* elements before the animation starts
*/
const beforeRemoveClass = (className: string | string[] | undefined) => { const beforeRemoveClass = (className: string | string[] | undefined) => {
beforeRemoveClasses = addClassToArray(beforeRemoveClasses, className); beforeRemoveClasses = addClassToArray(beforeRemoveClasses, className);
@ -245,40 +219,24 @@ export const createAnimation = () => {
return ani; return ani;
}; };
/**
* Add CSS class to the animation's
* elements after the animation ends.
*/
const afterAddClass = (className: string | string[] | undefined) => { const afterAddClass = (className: string | string[] | undefined) => {
afterAddClasses = addClassToArray(afterAddClasses, className); afterAddClasses = addClassToArray(afterAddClasses, className);
return ani; return ani;
}; };
/**
* Remove CSS class from the animation's
* elements after the animation ends.
*/
const afterRemoveClass = (className: string | string[] | undefined) => { const afterRemoveClass = (className: string | string[] | undefined) => {
afterRemoveClasses = addClassToArray(afterRemoveClasses, className); afterRemoveClasses = addClassToArray(afterRemoveClasses, className);
return ani; return ani;
}; };
/**
* Set CSS inline styles to the animation's
* elements after the animation ends.
*/
const afterStyles = (styles: { [property: string]: any } = {}) => { const afterStyles = (styles: { [property: string]: any } = {}) => {
afterStylesValue = styles; afterStylesValue = styles;
return ani; return ani;
}; };
/**
* Clear CSS inline styles from the animation's
* elements after the animation ends.
*/
const afterClearStyles = (propertyNames: string[] = []) => { const afterClearStyles = (propertyNames: string[] = []) => {
for (const property of propertyNames) { for (const property of propertyNames) {
afterStylesValue[property] = ''; afterStylesValue[property] = '';
@ -287,9 +245,6 @@ export const createAnimation = () => {
return ani; return ani;
}; };
/**
* Returns the animation's fill mode.
*/
const getFill = () => { const getFill = () => {
if (_fill !== undefined) { return _fill; } if (_fill !== undefined) { return _fill; }
if (parentAnimation) { return parentAnimation.getFill(); } if (parentAnimation) { return parentAnimation.getFill(); }
@ -297,9 +252,6 @@ export const createAnimation = () => {
return 'both'; return 'both';
}; };
/**
* Returns the animation's direction.
*/
const getDirection = () => { const getDirection = () => {
if (forceDirectionValue !== undefined) { return forceDirectionValue; } if (forceDirectionValue !== undefined) { return forceDirectionValue; }
if (_direction !== undefined) { return _direction; } if (_direction !== undefined) { return _direction; }
@ -309,9 +261,6 @@ export const createAnimation = () => {
}; };
/**
* Returns the animation's easing.
*/
const getEasing = () => { const getEasing = () => {
if (shouldForceLinearEasing) { return 'linear'; } if (shouldForceLinearEasing) { return 'linear'; }
if (_easing !== undefined) { return _easing; } if (_easing !== undefined) { return _easing; }
@ -320,9 +269,6 @@ export const createAnimation = () => {
return 'linear'; return 'linear';
}; };
/**
* Gets the animation's duration in milliseconds.
*/
const getDuration = () => { const getDuration = () => {
if (shouldForceSyncPlayback) { return 0; } if (shouldForceSyncPlayback) { return 0; }
if (forceDurationValue !== undefined) { return forceDurationValue; } if (forceDurationValue !== undefined) { return forceDurationValue; }
@ -332,9 +278,6 @@ export const createAnimation = () => {
return 0; return 0;
}; };
/**
* Gets the number of iterations the animation will run.
*/
const getIterations = () => { const getIterations = () => {
if (_iterations !== undefined) { return _iterations; } if (_iterations !== undefined) { return _iterations; }
if (parentAnimation) { return parentAnimation.getIterations(); } if (parentAnimation) { return parentAnimation.getIterations(); }
@ -342,9 +285,6 @@ export const createAnimation = () => {
return 1; return 1;
}; };
/**
* Gets the animation's delay in milliseconds.
*/
const getDelay = () => { const getDelay = () => {
if (forceDelayValue !== undefined) { return forceDelayValue; } if (forceDelayValue !== undefined) { return forceDelayValue; }
if (_delay !== undefined) { return _delay; } if (_delay !== undefined) { return _delay; }
@ -353,17 +293,10 @@ export const createAnimation = () => {
return 0; return 0;
}; };
/**
* Get an array of keyframes for the animation.
*/
const getKeyframes = () => { const getKeyframes = () => {
return _keyframes; return _keyframes;
}; };
/**
* Sets whether the animation should play forwards,
* backwards, or alternating back and forth.
*/
const direction = (animationDirection: AnimationDirection) => { const direction = (animationDirection: AnimationDirection) => {
_direction = animationDirection; _direction = animationDirection;
@ -372,22 +305,14 @@ export const createAnimation = () => {
return ani; return ani;
}; };
/**
* Sets how the animation applies styles to its
* elements before and after the animation's execution.
*/
const fill = (animationFill: AnimationFill) => { const fill = (animationFill: AnimationFill) => {
_fill = animationFill; _fill = animationFill;
update(true); update(true);
return ani; return ani;
}; };
/**
* Sets when an animation starts (in milliseconds).
*/
const delay = (animationDelay: number) => { const delay = (animationDelay: number) => {
_delay = animationDelay; _delay = animationDelay;
@ -396,10 +321,6 @@ export const createAnimation = () => {
return ani; return ani;
}; };
/**
* Sets how the animation progresses through the
* duration of each cycle.
*/
const easing = (animationEasing: string) => { const easing = (animationEasing: string) => {
_easing = animationEasing; _easing = animationEasing;
@ -408,10 +329,6 @@ export const createAnimation = () => {
return ani; return ani;
}; };
/**
* Sets the length of time the animation takes
* to complete one cycle.
*/
const duration = (animationDuration: number) => { const duration = (animationDuration: number) => {
/** /**
* CSS Animation Durations of 0ms work fine on Chrome * CSS Animation Durations of 0ms work fine on Chrome
@ -429,10 +346,6 @@ export const createAnimation = () => {
return ani; return ani;
}; };
/**
* Sets the number of times the animation cycle
* should be played before stopping.
*/
const iterations = (animationIterations: number) => { const iterations = (animationIterations: number) => {
_iterations = animationIterations; _iterations = animationIterations;
@ -441,18 +354,12 @@ export const createAnimation = () => {
return ani; return ani;
}; };
/** const parent = (animation: AnimationInternal) => {
* Sets the parent animation.
*/
const parent = (animation: Animation) => {
parentAnimation = animation; parentAnimation = animation;
return ani; return ani;
}; };
/**
* Add one or more elements to the animation
*/
const addElement = (el: Element | Element[] | Node | Node[] | NodeList | undefined | null) => { const addElement = (el: Element | Element[] | Node | Node[] | NodeList | undefined | null) => {
if (el != null) { if (el != null) {
@ -470,31 +377,22 @@ export const createAnimation = () => {
return ani; return ani;
}; };
/** const addAnimation = (animationToAdd: AnimationInternal | AnimationInternal[]) => {
* Group one or more animations together to be controlled by a parent animation. if ((animationToAdd as any) != null) {
*/ if (Array.isArray(animationToAdd)) {
const addAnimation = (animationToAdd: Animation | Animation[] | undefined | null) => { for (const animation of animationToAdd) {
if (animationToAdd != null) { animation.parent(ani);
const parentAnim = ani;
const animationsToAdd = animationToAdd as Animation[];
if (animationsToAdd.length >= 0) {
for (const animation of animationsToAdd) {
animation.parent(parentAnim);
childAnimations.push(animation); childAnimations.push(animation);
} }
} else { } else {
(animationToAdd as Animation).parent(parentAnim); animationToAdd.parent(ani);
childAnimations.push(animationToAdd as Animation); childAnimations.push(animationToAdd);
} }
} }
return ani; return ani;
}; };
/** const keyframes = (keyframeValues: AnimationKeyFrames) => {
* Set the keyframes for the animation.
*/
const keyframes = (keyframeValues: any[]) => {
_keyframes = keyframeValues; _keyframes = keyframeValues;
return ani; return ani;
@ -526,7 +424,7 @@ export const createAnimation = () => {
const removeClasses = beforeRemoveClasses; const removeClasses = beforeRemoveClasses;
const styles = beforeStylesValue; const styles = beforeStylesValue;
elements.forEach((el: HTMLElement) => { elements.forEach(el => {
const elementClassList = el.classList; const elementClassList = el.classList;
addClasses.forEach(c => elementClassList.add(c)); addClasses.forEach(c => elementClassList.add(c));
@ -575,7 +473,7 @@ export const createAnimation = () => {
const removeClasses = afterRemoveClasses; const removeClasses = afterRemoveClasses;
const styles = afterStylesValue; const styles = afterStylesValue;
elements.forEach((el: HTMLElement) => { elements.forEach(el => {
const elementClassList = el.classList; const elementClassList = el.classList;
addClasses.forEach(c => elementClassList.add(c)); addClasses.forEach(c => elementClassList.add(c));
@ -598,14 +496,14 @@ export const createAnimation = () => {
runAfterWrite(); runAfterWrite();
runAfterStyles(); runAfterStyles();
const didComplete = willComplete; const currentStep = willComplete ? 1 : 0;
onFinishCallbacks.forEach(onFinishCallback => { onFinishCallbacks.forEach(onFinishCallback => {
onFinishCallback.callback(didComplete, ani); return onFinishCallback.c(currentStep, ani);
}); });
onFinishOneTimeCallbacks.forEach(onFinishCallback => { onFinishOneTimeCallbacks.forEach(onFinishCallback => {
onFinishCallback.callback(didComplete, ani); return onFinishCallback.c(currentStep, ani);
}); });
onFinishOneTimeCallbacks.length = 0; onFinishOneTimeCallbacks.length = 0;
@ -637,16 +535,15 @@ export const createAnimation = () => {
const stylesheet = createKeyframeStylesheet(keyframeName, keyframeRules, element); const stylesheet = createKeyframeStylesheet(keyframeName, keyframeRules, element);
stylesheets.push(stylesheet); stylesheets.push(stylesheet);
setStyleProperty(element, 'animation-duration', (getDuration() !== undefined) ? `${getDuration()}ms` : null); setStyleProperty(element, 'animation-duration', `${getDuration()}ms`);
setStyleProperty(element, 'animation-timing-function', getEasing() || null); setStyleProperty(element, 'animation-timing-function', getEasing());
setStyleProperty(element, 'animation-delay', (getDelay() !== undefined) ? `${getDelay()}ms` : null); setStyleProperty(element, 'animation-delay', `${getDelay()}ms`);
setStyleProperty(element, 'animation-fill-mode', getFill() || null); setStyleProperty(element, 'animation-fill-mode', getFill());
setStyleProperty(element, 'animation-direction', getDirection() || null); setStyleProperty(element, 'animation-direction', getDirection());
const iterationsCount = const iterationsCount = (getIterations() === Infinity)
(getIterations() !== undefined) ? ? 'infinite'
(getIterations() === Infinity) ? 'infinite' : getIterations()!.toString() : getIterations().toString();
: null;
setStyleProperty(element, 'animation-iteration-count', iterationsCount); setStyleProperty(element, 'animation-iteration-count', iterationsCount);
setStyleProperty(element, 'animation-play-state', 'paused'); setStyleProperty(element, 'animation-play-state', 'paused');
@ -678,7 +575,7 @@ export const createAnimation = () => {
webAnimations.push(animation); webAnimations.push(animation);
}); });
if (getWebAnimations().length > 0) { if (webAnimations.length > 0) {
webAnimations[0].onfinish = () => { webAnimations[0].onfinish = () => {
animationFinish(); animationFinish();
}; };
@ -703,7 +600,7 @@ export const createAnimation = () => {
const setAnimationStep = (step: number) => { const setAnimationStep = (step: number) => {
step = Math.min(Math.max(step, 0), 0.999); step = Math.min(Math.max(step, 0), 0.999);
if (supportsWebAnimations) { if (supportsWebAnimations) {
getWebAnimations().forEach(animation => { webAnimations.forEach(animation => {
animation.currentTime = animation.effect.getComputedTiming().delay + (getDuration()! * step); animation.currentTime = animation.effect.getComputedTiming().delay + (getDuration()! * step);
animation.pause(); animation.pause();
}); });
@ -722,7 +619,7 @@ export const createAnimation = () => {
}; };
const updateWebAnimation = () => { const updateWebAnimation = () => {
getWebAnimations().forEach(animation => { webAnimations.forEach(animation => {
animation.effect.updateTiming({ animation.effect.updateTiming({
delay: getDelay(), delay: getDelay(),
duration: getDuration(), duration: getDuration(),
@ -738,16 +635,15 @@ export const createAnimation = () => {
elements.forEach(element => { elements.forEach(element => {
raf(() => { raf(() => {
setStyleProperty(element, 'animation-name', keyframeName || null); setStyleProperty(element, 'animation-name', keyframeName || null);
setStyleProperty(element, 'animation-duration', (getDuration() !== undefined) ? `${getDuration()}ms` : null); setStyleProperty(element, 'animation-duration', `${getDuration()}ms`);
setStyleProperty(element, 'animation-timing-function', getEasing() || null); setStyleProperty(element, 'animation-timing-function', getEasing());
setStyleProperty(element, 'animation-delay', (getDelay() !== undefined) ? `${getDelay()}ms` : null); setStyleProperty(element, 'animation-delay', `${getDelay()}ms`);
setStyleProperty(element, 'animation-fill-mode', getFill() || null); setStyleProperty(element, 'animation-fill-mode', getFill() || null);
setStyleProperty(element, 'animation-direction', getDirection() || null); setStyleProperty(element, 'animation-direction', getDirection() || null);
const iterationsCount = const iterationsCount = (getIterations() === Infinity)
(getIterations() !== undefined) ? ? 'infinite'
(getIterations() === Infinity) ? 'infinite' : getIterations()!.toString() : getIterations().toString();
: null;
setStyleProperty(element, 'animation-iteration-count', iterationsCount); setStyleProperty(element, 'animation-iteration-count', iterationsCount);
@ -762,9 +658,6 @@ export const createAnimation = () => {
}); });
}; };
/**
* Updates any existing animations.
*/
const update = (deep = false, toggleAnimationName = true) => { const update = (deep = false, toggleAnimationName = true) => {
if (deep) { if (deep) {
childAnimations.forEach(animation => { childAnimations.forEach(animation => {
@ -803,19 +696,15 @@ export const createAnimation = () => {
childAnimations.forEach(animation => { childAnimations.forEach(animation => {
animation.progressStep(step); animation.progressStep(step);
}); });
if (getDuration() !== undefined) {
setAnimationStep(step); setAnimationStep(step);
}
return ani; return ani;
}; };
const progressEnd = (shouldComplete: boolean, step: number, dur: number | undefined) => { const progressEnd = (playTo: 0 | 1 | undefined, step: number, dur?: number) => {
shouldForceLinearEasing = false; shouldForceLinearEasing = false;
childAnimations.forEach(animation => { childAnimations.forEach(animation => {
animation.progressEnd(shouldComplete, step, dur); animation.progressEnd(playTo, step, dur);
}); });
if (dur !== undefined) { if (dur !== undefined) {
@ -824,9 +713,9 @@ export const createAnimation = () => {
finished = false; finished = false;
willComplete = shouldComplete; willComplete = playTo === 1;
if (!shouldComplete) { if (!willComplete) {
forceDirectionValue = (getDirection() === 'reverse') ? 'normal' : 'reverse'; forceDirectionValue = (getDirection() === 'reverse') ? 'normal' : 'reverse';
if (supportsWebAnimations) { if (supportsWebAnimations) {
@ -862,7 +751,7 @@ export const createAnimation = () => {
const pauseAnimation = () => { const pauseAnimation = () => {
if (initialized) { if (initialized) {
if (supportsWebAnimations) { if (supportsWebAnimations) {
getWebAnimations().forEach(animation => { webAnimations.forEach(animation => {
animation.pause(); animation.pause();
}); });
} else { } else {
@ -873,9 +762,6 @@ export const createAnimation = () => {
} }
}; };
/**
* Pause the animation.
*/
const pause = () => { const pause = () => {
childAnimations.forEach(animation => { childAnimations.forEach(animation => {
animation.pause(); animation.pause();
@ -886,30 +772,12 @@ export const createAnimation = () => {
return ani; return ani;
}; };
/**
* Play the animation asynchronously.
* This returns a promise that resolves
* when the animation has ended.
*/
const playAsync = () => { const playAsync = () => {
return new Promise(resolve => { return play();
onFinish(resolve, { oneTimeCallback: true });
play();
return ani;
});
}; };
/**
* Play the animation synchronously. This
* is the equivalent of running the animation
* with a duration of 0ms.
*/
const playSync = () => { const playSync = () => {
shouldForceSyncPlayback = true; play({ sync: true });
onFinish(() => shouldForceSyncPlayback = false, { oneTimeCallback: true });
play();
return ani; return ani;
}; };
@ -984,7 +852,7 @@ export const createAnimation = () => {
}; };
const playWebAnimations = () => { const playWebAnimations = () => {
getWebAnimations().forEach(animation => { webAnimations.forEach(animation => {
animation.play(); animation.play();
}); });
@ -1001,10 +869,13 @@ export const createAnimation = () => {
} }
}; };
/** const play = (opts?: AnimationPlayOptions) => {
* Play the animation return new Promise<void>(resolve => {
*/ if (opts && opts.sync) {
const play = () => { shouldForceSyncPlayback = true;
onFinish(() => shouldForceSyncPlayback = false, { oneTimeCallback: true });
}
if (!initialized) { if (!initialized) {
initializeAnimation(); initializeAnimation();
} }
@ -1019,6 +890,8 @@ export const createAnimation = () => {
shouldCalculateNumAnimations = false; shouldCalculateNumAnimations = false;
} }
onFinish(() => resolve(), { oneTimeCallback: true });
childAnimations.forEach(animation => { childAnimations.forEach(animation => {
animation.play(); animation.play();
}); });
@ -1028,14 +901,9 @@ export const createAnimation = () => {
} else { } else {
playCSSAnimations(); playCSSAnimations();
} }
});
return ani;
}; };
/**
* Stop the animation and reset
* all elements to their initial state
*/
const stop = () => { const stop = () => {
childAnimations.forEach(animation => { childAnimations.forEach(animation => {
animation.stop(); animation.stop();
@ -1045,14 +913,12 @@ export const createAnimation = () => {
cleanUpElements(); cleanUpElements();
initialized = false; initialized = false;
} }
return ani;
}; };
const from = (property: string, value: any) => { const from = (property: string, value: any) => {
const firstFrame = _keyframes[0]; const firstFrame = _keyframes[0] as AnimationKeyFrame | undefined;
if (firstFrame != null && (firstFrame.offset === undefined || firstFrame.offset === 0)) { if (firstFrame !== undefined && firstFrame.offset === 0) {
firstFrame[property] = value; firstFrame[property] = value;
} else { } else {
_keyframes = [ _keyframes = [
@ -1065,9 +931,9 @@ export const createAnimation = () => {
}; };
const to = (property: string, value: any) => { const to = (property: string, value: any) => {
const lastFrame = _keyframes[_keyframes.length - 1]; const lastFrame = _keyframes[_keyframes.length - 1] as AnimationKeyFrame | undefined;
if (lastFrame != null && (lastFrame.offset === undefined || lastFrame.offset === 1)) { if (lastFrame !== undefined && lastFrame.offset === 1) {
lastFrame[property] = value; lastFrame[property] = value;
} else { } else {
_keyframes = [ _keyframes = [
@ -1128,10 +994,9 @@ export const createAnimation = () => {
beforeRemoveClass, beforeRemoveClass,
beforeAddClass, beforeAddClass,
onFinish, onFinish,
clearOnFinish,
progressStart, progressStart,
progressStep, progressStep,
progressEnd progressEnd
} as Animation; };
}; };

View File

@ -1,10 +1,11 @@
import { createAnimation } from '../animation'; import { createAnimation } from '../animation';
import { getTimeGivenProgression, Point } from '../cubic-bezier'; import { getTimeGivenProgression, Point } from '../cubic-bezier';
import { Animation } from '../animation-interface';
describe('Animation Class', () => { describe('Animation Class', () => {
describe('addElement()', () => { describe('addElement()', () => {
let animation; let animation: Animation;
beforeEach(() => { beforeEach(() => {
animation = createAnimation(); animation = createAnimation();
}); });
@ -40,7 +41,7 @@ describe('Animation Class', () => {
}); });
describe('addAnimation()', () => { describe('addAnimation()', () => {
let animation; let animation: Animation;
beforeEach(() => { beforeEach(() => {
animation = createAnimation(); animation = createAnimation();
}); });
@ -67,9 +68,9 @@ describe('Animation Class', () => {
}); });
describe('Animation Keyframes', () => { describe('Animation Keyframes', () => {
let animation; let animation: Animation;
beforeEach(() => { beforeEach(() => {
animation = createAnimation('my-animation'); animation = createAnimation();
}); });
it('should generate a keyframe', () => { it('should generate a keyframe', () => {
@ -146,7 +147,7 @@ describe('Animation Class', () => {
}); });
describe('Animation Config Methods', () => { describe('Animation Config Methods', () => {
let animation; let animation: Animation;
beforeEach(() => { beforeEach(() => {
animation = createAnimation(); animation = createAnimation();
}); });
@ -258,16 +259,16 @@ describe('Animation Class', () => {
}); });
it('should get "both" when fill not set', () => { it('should get "both" when fill not set', () => {
expect(animation.getFill()).toEqual("both"); expect(animation.getFill()).toEqual('both');
}); });
it('should get parent fill when child fill is not set', () => { it('should get parent fill when child fill is not set', () => {
const childAnimation = createAnimation(); const childAnimation = createAnimation();
animation animation
.addAnimation(childAnimation) .addAnimation(childAnimation)
.fill('forwards'); .fill('both');
expect(childAnimation.getFill()).toEqual('forwards'); expect(childAnimation.getFill()).toEqual('both');
}); });
it('should get prefer child fill over parent fill', () => { it('should get prefer child fill over parent fill', () => {
@ -282,7 +283,7 @@ describe('Animation Class', () => {
}); });
it('should get "normal" when direction not set', () => { it('should get "normal" when direction not set', () => {
expect(animation.getDirection()).toEqual('normal'); expect(animation.getDirection()).toEqual("normal");
}); });
it('should get parent direction when child direction is not set', () => { it('should get parent direction when child direction is not set', () => {

View File

@ -29,11 +29,11 @@ export const menuOverlayAnimation = (menu: MenuI): IonicAnimation => {
} }
menuAnimation menuAnimation
.addElement(menu.menuInnerEl) .addElement(menu.menuInnerEl!)
.fromTo('transform', `translateX(${closedX})`, `translateX(${openedX})`); .fromTo('transform', `translateX(${closedX})`, `translateX(${openedX})`);
backdropAnimation backdropAnimation
.addElement(menu.backdropEl) .addElement(menu.backdropEl!)
.fromTo('opacity', 0.01, 0.32); .fromTo('opacity', 0.01, 0.32);
return baseAnimation().addAnimation([menuAnimation, backdropAnimation]); return baseAnimation().addAnimation([menuAnimation, backdropAnimation]);

View File

@ -24,15 +24,15 @@ export const menuPushAnimation = (menu: MenuI): IonicAnimation => {
} }
const menuAnimation = createAnimation() const menuAnimation = createAnimation()
.addElement(menu.menuInnerEl) .addElement(menu.menuInnerEl!)
.fromTo('transform', `translateX(${menuClosedX})`, 'translateX(0px)'); .fromTo('transform', `translateX(${menuClosedX})`, 'translateX(0px)');
const contentAnimation = createAnimation() const contentAnimation = createAnimation()
.addElement(menu.contentEl) .addElement(menu.contentEl!)
.fromTo('transform', 'translateX(0px)', `translateX(${contentOpenedX})`); .fromTo('transform', 'translateX(0px)', `translateX(${contentOpenedX})`);
const backdropAnimation = createAnimation() const backdropAnimation = createAnimation()
.addElement(menu.backdropEl) .addElement(menu.backdropEl!)
.fromTo('opacity', 0.01, 0.32); .fromTo('opacity', 0.01, 0.32);
return baseAnimation().addAnimation([menuAnimation, backdropAnimation, contentAnimation]); return baseAnimation().addAnimation([menuAnimation, backdropAnimation, contentAnimation]);

View File

@ -12,7 +12,7 @@ export const menuRevealAnimation = (menu: MenuI): IonicAnimation => {
const openedX = (menu.width * (menu.isEndSide ? -1 : 1)) + 'px'; const openedX = (menu.width * (menu.isEndSide ? -1 : 1)) + 'px';
const contentOpen = createAnimation() const contentOpen = createAnimation()
.addElement(menu.contentEl) .addElement(menu.contentEl!) // REVIEW
.fromTo('transform', 'translateX(0px)', `translateX(${openedX})`); .fromTo('transform', 'translateX(0px)', `translateX(${openedX})`);
return baseAnimation().addAnimation(contentOpen); return baseAnimation().addAnimation(contentOpen);

View File

@ -165,7 +165,7 @@ const createMenuController = () => {
return menu._setOpen(shouldOpen, animated); return menu._setOpen(shouldOpen, animated);
}; };
const _createAnimation = (type: string, menuCmp: MenuI): Promise<any> => { const _createAnimation = (type: string, menuCmp: MenuI) => {
const animationBuilder = menuAnimations.get(type) as any; const animationBuilder = menuAnimations.get(type) as any;
if (!animationBuilder) { if (!animationBuilder) {
throw new Error('animation not registered'); throw new Error('animation not registered');

View File

@ -7,7 +7,10 @@ const iosTransitionAnimation = () => import('./ios.transition');
const mdTransitionAnimation = () => import('./md.transition'); const mdTransitionAnimation = () => import('./md.transition');
// TODO: Remove when removing AnimationBuilder // TODO: Remove when removing AnimationBuilder
export type IonicAnimationInterface = ((navEl: HTMLElement, opts: TransitionOptions) => IonicAnimation) | ((navEl: HTMLElement, opts: TransitionOptions) => Promise<IonicAnimation>); export type IonicAnimationInterface = (
((navEl: HTMLElement, opts: TransitionOptions) => IonicAnimation) |
((navEl: HTMLElement, opts: TransitionOptions) => Promise<IonicAnimation>)
);
export const transition = (opts: TransitionOptions): Promise<TransitionResult> => { export const transition = (opts: TransitionOptions): Promise<TransitionResult> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -95,19 +98,16 @@ const animation = async (animationBuilder: IonicAnimationInterface | AnimationBu
const didComplete = await playTransition(trans, opts); const didComplete = await playTransition(trans, opts);
// TODO: Remove AnimationBuilder
(trans as any).hasCompleted = didComplete;
if (opts.progressCallback) { if (opts.progressCallback) {
opts.progressCallback(undefined); opts.progressCallback(undefined);
} }
if ((trans as any).hasCompleted) { if (didComplete) {
fireDidEvents(opts.enteringEl, opts.leavingEl); fireDidEvents(opts.enteringEl, opts.leavingEl);
} }
return { return {
hasCompleted: (trans as any).hasCompleted, hasCompleted: didComplete,
animation: trans animation: trans
}; };
}; };
@ -146,11 +146,19 @@ const notifyViewReady = async (viewIsReady: undefined | ((enteringEl: HTMLElemen
} }
}; };
const playTransition = (trans: IonicAnimation | Animation, opts: TransitionOptions): Promise<Animation | boolean> => { const playTransition = (trans: IonicAnimation | Animation, opts: TransitionOptions): Promise<boolean> => {
const progressCallback = opts.progressCallback; const progressCallback = opts.progressCallback;
// TODO: Remove AnimationBuilder // TODO: Remove AnimationBuilder
const promise = new Promise<Animation | boolean>(resolve => trans.onFinish(resolve)); const promise = new Promise<boolean>(resolve => {
trans.onFinish((currentStep: any) => {
if (typeof currentStep === 'number') {
resolve(currentStep === 1);
} else if ((trans as any).hasCompleted !== undefined) {
resolve((trans as Animation).hasCompleted);
}
});
});
// cool, let's do this, start the transition // cool, let's do this, start the transition
if (progressCallback) { if (progressCallback) {

View File

@ -214,9 +214,9 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio
} }
if (!contentEl && enteringToolBarEls.length === 0 && headerEls.length === 0) { if (!contentEl && enteringToolBarEls.length === 0 && headerEls.length === 0) {
enteringContentAnimation.addElement(enteringEl.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs')); enteringContentAnimation.addElement(enteringEl.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs')!); // REVIEW
} else { } else {
enteringContentAnimation.addElement(contentEl); enteringContentAnimation.addElement(contentEl!); // REVIEW
enteringContentAnimation.addElement(headerEls); enteringContentAnimation.addElement(headerEls);
} }
@ -250,12 +250,12 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio
.afterStyles({ opacity: '' }); .afterStyles({ opacity: '' });
enteringTransitionCover enteringTransitionCover
.addElement(enteringTransitionCoverEl) .addElement(enteringTransitionCoverEl!) // REVIEW
.beforeClearStyles([OPACITY]) .beforeClearStyles([OPACITY])
.fromTo(OPACITY, 0, 0.1); .fromTo(OPACITY, 0, 0.1);
enteringTransitionShadow enteringTransitionShadow
.addElement(enteringTransitionShadowEl) .addElement(enteringTransitionShadowEl!) // REVIEW
.beforeClearStyles([OPACITY]) .beforeClearStyles([OPACITY])
.fromTo(OPACITY, 0.03, 0.70); .fromTo(OPACITY, 0.03, 0.70);
@ -274,7 +274,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio
rootAnimation.addAnimation(enteringToolBar); rootAnimation.addAnimation(enteringToolBar);
const enteringTitle = createAnimation(); const enteringTitle = createAnimation();
enteringTitle.addElement(enteringToolBarEl.querySelector('ion-title')); enteringTitle.addElement(enteringToolBarEl.querySelector('ion-title')!); // REVIEW
const enteringToolBarButtons = createAnimation(); const enteringToolBarButtons = createAnimation();
const buttons = Array.from(enteringToolBarEl.querySelectorAll('ion-buttons,[menuToggle]')); const buttons = Array.from(enteringToolBarEl.querySelectorAll('ion-buttons,[menuToggle]'));
@ -298,7 +298,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio
enteringToolBarItems.addElement(enteringToolBarEl.querySelectorAll(':scope > *:not(ion-title):not(ion-buttons):not([menuToggle])')); enteringToolBarItems.addElement(enteringToolBarEl.querySelectorAll(':scope > *:not(ion-title):not(ion-buttons):not([menuToggle])'));
const enteringToolBarBg = createAnimation(); const enteringToolBarBg = createAnimation();
enteringToolBarBg.addElement(shadow(enteringToolBarEl).querySelector('.toolbar-background')); enteringToolBarBg.addElement(shadow(enteringToolBarEl).querySelector('.toolbar-background')!); // REVIEW
const enteringBackButton = createAnimation(); const enteringBackButton = createAnimation();
const backButtonEl = enteringToolBarEl.querySelector('ion-back-button'); const backButtonEl = enteringToolBarEl.querySelector('ion-back-button');
@ -345,7 +345,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio
if (backButtonEl && !forward) { if (backButtonEl && !forward) {
const enteringBackBtnText = createAnimation(); const enteringBackBtnText = createAnimation();
enteringBackBtnText enteringBackBtnText
.addElement(shadow(backButtonEl).querySelector('.button-text')) .addElement(shadow(backButtonEl).querySelector('.button-text')!) // REVIEW
.fromTo(`transform`, (isRTL ? 'translateX(-100px)' : 'translateX(100px)'), 'translateX(0px)'); .fromTo(`transform`, (isRTL ? 'translateX(-100px)' : 'translateX(100px)'), 'translateX(0px)');
enteringToolBar.addAnimation(enteringBackBtnText); enteringToolBar.addAnimation(enteringBackBtnText);
@ -359,7 +359,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio
const leavingContent = createAnimation(); const leavingContent = createAnimation();
const leavingContentEl = leavingEl.querySelector(':scope > ion-content'); const leavingContentEl = leavingEl.querySelector(':scope > ion-content');
leavingContent.addElement(leavingContentEl); leavingContent.addElement(leavingContentEl!); // REVIEW
leavingContent.addElement(leavingEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *')); leavingContent.addElement(leavingEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *'));
rootAnimation.addAnimation(leavingContent); rootAnimation.addAnimation(leavingContent);
@ -393,12 +393,12 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio
.afterStyles({ opacity: '' }); .afterStyles({ opacity: '' });
leavingTransitionCover leavingTransitionCover
.addElement(leavingTransitionCoverEl) .addElement(leavingTransitionCoverEl!) // REVIEW
.beforeClearStyles([OPACITY]) .beforeClearStyles([OPACITY])
.fromTo(OPACITY, 0.1, 0); .fromTo(OPACITY, 0.1, 0);
leavingTransitionShadow leavingTransitionShadow
.addElement(leavingTransitionShadowEl) .addElement(leavingTransitionShadowEl!) // REVIEW
.beforeClearStyles([OPACITY]) .beforeClearStyles([OPACITY])
.fromTo(OPACITY, 0.70, 0.03); .fromTo(OPACITY, 0.70, 0.03);
@ -413,7 +413,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio
leavingToolBar.addElement(leavingToolBarEl); leavingToolBar.addElement(leavingToolBarEl);
const leavingTitle = createAnimation(); const leavingTitle = createAnimation();
leavingTitle.addElement(leavingToolBarEl.querySelector('ion-title')); leavingTitle.addElement(leavingToolBarEl.querySelector('ion-title')!); // REVIEW
const leavingToolBarButtons = createAnimation(); const leavingToolBarButtons = createAnimation();
const buttons = leavingToolBarEl.querySelectorAll('ion-buttons,[menuToggle]'); const buttons = leavingToolBarEl.querySelectorAll('ion-buttons,[menuToggle]');
@ -435,7 +435,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio
} }
const leavingToolBarBg = createAnimation(); const leavingToolBarBg = createAnimation();
leavingToolBarBg.addElement(shadow(leavingToolBarEl).querySelector('.toolbar-background')); leavingToolBarBg.addElement(shadow(leavingToolBarEl).querySelector('.toolbar-background')!); // REVIEW
const leavingBackButton = createAnimation(); const leavingBackButton = createAnimation();
const backButtonEl = leavingToolBarEl.querySelector('ion-back-button'); const backButtonEl = leavingToolBarEl.querySelector('ion-back-button');
@ -472,7 +472,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio
if (backButtonEl && !backward) { if (backButtonEl && !backward) {
const leavingBackBtnText = createAnimation(); const leavingBackBtnText = createAnimation();
leavingBackBtnText leavingBackBtnText
.addElement(shadow(backButtonEl).querySelector('.button-text')) .addElement(shadow(backButtonEl).querySelector('.button-text')!) // REVIEW
.fromTo('transform', `translateX(${CENTER})`, `translateX(${(isRTL ? -124 : 124) + 'px'})`); .fromTo('transform', `translateX(${CENTER})`, `translateX(${(isRTL ? -124 : 124) + 'px'})`);
leavingToolBar.addAnimation(leavingBackBtnText); leavingToolBar.addAnimation(leavingBackBtnText);
} }