From b6392af33f2e652ad6500ec9c9817df3be782e25 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 23 Jul 2019 16:22:02 -0400 Subject: [PATCH] Add playSync --- core/src/utils/animation/animation.ts | 178 +++++++++++++++++++------- 1 file changed, 132 insertions(+), 46 deletions(-) diff --git a/core/src/utils/animation/animation.ts b/core/src/utils/animation/animation.ts index 1b3dec72d8..5e6ba95de3 100644 --- a/core/src/utils/animation/animation.ts +++ b/core/src/utils/animation/animation.ts @@ -1,6 +1,8 @@ // TODO: Add validation // TODO: More tests + let counter = 0; + export interface Animation { parentAnimation: Animation | undefined; elements: HTMLElement[]; @@ -15,6 +17,8 @@ export interface Animation { animationFinish(): void; play(): Animation; + playAsync(): Promise; + playSync(): Animation; pause(): Animation; stop(): Animation; destroy(): Animation; @@ -48,10 +52,15 @@ export interface Animation { getEasing(): string | undefined; getDuration(): number | undefined; + afterAddRead(readFn: () => void): Animation; + afterAddWrite(writeFn: () => void): Animation; afterClearStyles(propertyNames: string[]): Animation; afterStyles(styles: { [property: string]: any }): Animation; afterRemoveClass(className: string | string[] | undefined): Animation; afterAddClass(className: string | string[] | undefined): Animation; + + beforeAddRead(readFn: () => void): Animation; + beforeAddWrite(writeFn: () => void): Animation; beforeClearStyles(propertyNames: string[]): Animation; beforeStyles(styles: { [property: string]: any }): Animation; beforeRemoveClass(className: string | string[] | undefined): Animation; @@ -91,8 +100,7 @@ const animationEnd = (el: HTMLElement | null, callback: (ev?: TransitionEvent) = }; const supportsWebAnimations = (): boolean => { - return false; - //return !!(window as any).Animation; + return !!(window as any).Animation; }; const generateKeyframeString = (name: string | undefined, keyframes: any[] = []): string => { @@ -112,8 +120,8 @@ const generateKeyframeString = (name: string | undefined, keyframes: any[] = []) keyframeString.push(`${offset * 100}% { ${frameString.join(' ')} }`); }); - - keyframeString.push('}') + + keyframeString.push('}'); return keyframeString.join(' '); }; @@ -123,13 +131,13 @@ const createKeyframeStylesheet = (name: string | undefined, keyframeString: stri const stylesheet = document.createElement('style'); stylesheet.id = stylesheetId; stylesheet.appendChild(document.createTextNode(keyframeString)); - + const rootNode = (element.getRootNode() as any); const styleContainer = (rootNode.head || rootNode); - + const existingStylesheet = rootNode.querySelector(`#${stylesheetId}`); - if (existingStylesheet) { return } - + if (existingStylesheet) { return; } + styleContainer.appendChild(stylesheet); return stylesheet; @@ -145,7 +153,7 @@ const addClassToArray = (classes: string[] = [], className: string | string[] | return classes; }; -export const createAnimation = (animationNameValue: string | undefined): Animation => { +export const createAnimation = (): Animation => { let elements: HTMLElement[] = []; let childAnimations: Animation[] = []; let _name: string | undefined; @@ -153,13 +161,13 @@ export const createAnimation = (animationNameValue: string | undefined): Animati let _duration: number | undefined; let _easing: string | undefined; let _iterations: number | undefined; - let _fill: 'auto' | 'none' | 'forwards' | 'backwards' | 'both' | undefined; + let _fill: 'auto' | 'none' | 'forwards' | 'backwards' | 'both' | undefined = 'forwards'; let _direction: 'normal' | 'reverse' | 'alternate' | 'alternate-reverse' | undefined; let _keyframes: any[] = []; let initialized = false; - let stylesheets: HTMLElement[] | undefined = []; + let stylesheets: HTMLElement[] = []; let parentAnimation: Animation | undefined; @@ -177,6 +185,12 @@ export const createAnimation = (animationNameValue: string | undefined): Animati let numAnimationsRunning = 0; let shouldForceLinearEasing = false; + let shouldForceSyncPlayback = false; + + const _beforeAddReadFunctions: any[] = []; + const _beforeAddWriteFunctions: any[] = []; + const _afterAddReadFunctions: any[] = []; + const _afterAddWriteFunctions: any[] = []; /** * Destroy this animation and all child animations. @@ -232,10 +246,34 @@ export const createAnimation = (animationNameValue: string | undefined): Animati stylesheets.forEach(stylesheet => { stylesheet.parentNode!.removeChild(stylesheet); }); - + stylesheets = []; }; + const beforeAddRead = (readFn: () => void): Animation => { + _beforeAddReadFunctions.push(readFn); + + return generatePublicAPI(); + }; + + const beforeAddWrite = (writeFn: () => void): Animation => { + _beforeAddWriteFunctions.push(writeFn); + + return generatePublicAPI(); + }; + + const afterAddRead = (readFn: () => void): Animation => { + _afterAddReadFunctions.push(readFn); + + return generatePublicAPI(); + }; + + const afterAddWrite = (writeFn: () => void): Animation => { + _afterAddWriteFunctions.push(writeFn); + + return generatePublicAPI(); + }; + /** * Add CSS class to this animation's elements * before the animation begins. @@ -344,6 +382,7 @@ export const createAnimation = (animationNameValue: string | undefined): Animati }; const getDuration = (): number | undefined => { + if (shouldForceSyncPlayback) { return 0; } if (_duration !== undefined) { return _duration; } if (parentAnimation) { return parentAnimation.getDuration(); } @@ -370,7 +409,6 @@ export const createAnimation = (animationNameValue: string | undefined): Animati const name = (animationName: string): Animation => { _name = animationName; - counter += 1; return generatePublicAPI(); }; @@ -463,7 +501,19 @@ export const createAnimation = (animationNameValue: string | undefined): Animati return generatePublicAPI(); }; - const beforeAnimation = () => { + const runBeforeRead = () => { + _beforeAddReadFunctions.forEach(callback => { + callback(); + }); + }; + + const runBeforeWrite = () => { + _beforeAddWriteFunctions.forEach(callback => { + callback(); + }); + }; + + const runBeforeStyles = () => { const addClasses = beforeAddClasses; const removeClasses = beforeRemoveClasses; const styles = beforeStylesValue; @@ -482,7 +532,25 @@ export const createAnimation = (animationNameValue: string | undefined): Animati }); }; - const afterAnimation = () => { + const beforeAnimation = () => { + runBeforeRead(); + runBeforeWrite(); + runBeforeStyles(); + }; + + const runAfterRead = () => { + _afterAddReadFunctions.forEach(callback => { + callback(); + }); + }; + + const runAfterWrite = () => { + _afterAddWriteFunctions.forEach(callback => { + callback(); + }); + }; + + const runAfterStyles = () => { const addClasses = afterAddClasses; const removeClasses = afterRemoveClasses; const styles = afterStylesValue; @@ -499,6 +567,12 @@ export const createAnimation = (animationNameValue: string | undefined): Animati } } }); + }; + + const afterAnimation = () => { + runAfterRead(); + runAfterWrite(); + runAfterStyles(); if (onFinishCallback !== undefined) { onFinishCallback(generatePublicAPI()); @@ -520,32 +594,31 @@ export const createAnimation = (animationNameValue: string | undefined): Animati }; const initializeCSSAnimation = () => { - + elements.forEach(element => { if (_keyframes.length > 0) { - + const stylesheet = createKeyframeStylesheet(_name, generateKeyframeString(_name, _keyframes), element); if (stylesheet) { stylesheets.push(stylesheet); } - + element.style.setProperty('animation-name', _name || null); element.style.setProperty('animation-duration', (getDuration() !== undefined) ? `${getDuration()}ms` : null); element.style.setProperty('animation-timing-function', getEasing() || null); element.style.setProperty('animation-delay', (getDelay() !== undefined) ? `${getDelay()}ms` : null); element.style.setProperty('animation-fill-mode', getFill() || null); element.style.setProperty('animation-direction', getDirection() || null); - + let iterationsCount = null; if (getIterations() !== undefined) { iterationsCount = (getIterations() === Infinity) ? 'infinite' : getIterations()!.toString(); } - + element.style.setProperty('animation-iteration-countion', iterationsCount); element.style.setProperty('animation-play-state', 'paused'); } }); - if (elements.length > 0) { animationEnd(elements[0], () => { @@ -605,7 +678,7 @@ export const createAnimation = (animationNameValue: string | undefined): Animati childAnimations.forEach(animation => { animation.progressStep(step); }); - + if (getDuration() !== undefined) { if (supportsWebAnimations()) { webAnimations.forEach(animation => { @@ -634,21 +707,18 @@ export const createAnimation = (animationNameValue: string | undefined): Animati shouldForceLinearEasing = forceLinearEasing; - if (!initialized) { - initializeAnimation(); - } + initializeAnimation(); return generatePublicAPI(); }; const progressEnd = (shouldComplete: boolean, step: number): Animation => { - shouldComplete; - step; + console.log(shouldComplete, step); shouldForceLinearEasing = false; - + // temp - //play(); + play(); return generatePublicAPI(); }; @@ -673,15 +743,31 @@ export const createAnimation = (animationNameValue: string | undefined): Animati return generatePublicAPI(); }; + const playAsync = (): Promise => { + return new Promise(resolve => { + onFinish(resolve); + play(); + + return generatePublicAPI(); + }); + }; + + const playSync = (): Animation => { + shouldForceSyncPlayback = true; + + onFinish(() => shouldForceSyncPlayback = false); + play(); + + return generatePublicAPI(); + }; + const play = (): Animation => { childAnimations.forEach(animation => { animation.play(); }); - - if (!initialized) { - initializeAnimation(); - } - + + initializeAnimation(); + if (supportsWebAnimations()) { webAnimations.forEach(animation => { animation.play(); @@ -689,7 +775,7 @@ export const createAnimation = (animationNameValue: string | undefined): Animati } else { elements.forEach(element => { if (_keyframes.length > 0) { - (element as HTMLElement).style.animationPlayState = 'running';} + (element as HTMLElement).style.animationPlayState = 'running'; } }); } @@ -731,19 +817,19 @@ export const createAnimation = (animationNameValue: string | undefined): Animati }; const to = (property: string, value: any): Animation => { - + const keyframeValues = getKeyframes(); const lastFrame = keyframeValues[keyframeValues.length - 1]; if (lastFrame != null && (lastFrame.offset === undefined || lastFrame.offset === 1)) { lastFrame[property] = value; } else { - + const object: any = { offset: 1 }; object[property] = value; - + _keyframes = [ ..._keyframes, object @@ -776,6 +862,8 @@ export const createAnimation = (animationNameValue: string | undefined): Animati fromTo, parent, play, + playAsync, + playSync, pause, stop, destroy, @@ -797,10 +885,14 @@ export const createAnimation = (animationNameValue: string | undefined): Animati getIterations, getEasing, getDuration, + afterAddRead, + afterAddWrite, afterClearStyles, afterStyles, afterRemoveClass, afterAddClass, + beforeAddRead, + beforeAddWrite, beforeClearStyles, beforeStyles, beforeRemoveClass, @@ -812,15 +904,9 @@ export const createAnimation = (animationNameValue: string | undefined): Animati progressEnd }; }; - animationNameValue; - - name(`ion-animation-${counter}`); -/* - if (animationNameValue !== undefined) { - name(animationNameValue); - } -*/ + name(`ion-animation-${counter}`); + counter++; return generatePublicAPI(); };