UNPKG

@huluvu424242/honey-slideshow

Version:

Text to Speech component wich is reading texts from DOM elements.

956 lines (952 loc) 32.4 kB
import { r as raf } from './helpers-29d709d1.js'; /** * Web Animations requires hyphenated CSS properties * to be written in camelCase when animating */ const processKeyframes = (keyframes) => { keyframes.forEach(keyframe => { for (const key in keyframe) { if (keyframe.hasOwnProperty(key)) { const value = keyframe[key]; if (key === 'easing') { const newKey = 'animation-timing-function'; keyframe[newKey] = value; delete keyframe[key]; } else { const newKey = convertCamelCaseToHypen(key); if (newKey !== key) { keyframe[newKey] = value; delete keyframe[key]; } } } } }); return keyframes; }; const convertCamelCaseToHypen = (str) => { return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); }; let animationPrefix; const getAnimationPrefix = (el) => { if (animationPrefix === undefined) { const supportsUnprefixed = el.style.animationName !== undefined; const supportsWebkitPrefix = el.style.webkitAnimationName !== undefined; animationPrefix = (!supportsUnprefixed && supportsWebkitPrefix) ? '-webkit-' : ''; } return animationPrefix; }; const setStyleProperty = (element, propertyName, value) => { const prefix = propertyName.startsWith('animation') ? getAnimationPrefix(element) : ''; element.style.setProperty(prefix + propertyName, value); }; const removeStyleProperty = (element, propertyName) => { const prefix = propertyName.startsWith('animation') ? getAnimationPrefix(element) : ''; element.style.removeProperty(prefix + propertyName); }; const animationEnd = (el, callback) => { let unRegTrans; const opts = { passive: true }; const unregister = () => { if (unRegTrans) { unRegTrans(); } }; const onTransitionEnd = (ev) => { if (el === ev.target) { unregister(); callback(ev); } }; if (el) { el.addEventListener('webkitAnimationEnd', onTransitionEnd, opts); el.addEventListener('animationend', onTransitionEnd, opts); unRegTrans = () => { el.removeEventListener('webkitAnimationEnd', onTransitionEnd, opts); el.removeEventListener('animationend', onTransitionEnd, opts); }; } return unregister; }; const generateKeyframeRules = (keyframes = []) => { return keyframes.map(keyframe => { const offset = keyframe.offset; const frameString = []; for (const property in keyframe) { if (keyframe.hasOwnProperty(property) && property !== 'offset') { frameString.push(`${property}: ${keyframe[property]};`); } } return `${offset * 100}% { ${frameString.join(' ')} }`; }).join(' '); }; const keyframeIds = []; const generateKeyframeName = (keyframeRules) => { let index = keyframeIds.indexOf(keyframeRules); if (index < 0) { index = (keyframeIds.push(keyframeRules) - 1); } return `ion-animation-${index}`; }; const getStyleContainer = (element) => { const rootNode = element.getRootNode(); return (rootNode.head || rootNode); }; const createKeyframeStylesheet = (keyframeName, keyframeRules, element) => { const styleContainer = getStyleContainer(element); const keyframePrefix = getAnimationPrefix(element); const existingStylesheet = styleContainer.querySelector('#' + keyframeName); if (existingStylesheet) { return existingStylesheet; } const stylesheet = (element.ownerDocument || document).createElement('style'); stylesheet.id = keyframeName; stylesheet.textContent = `@${keyframePrefix}keyframes ${keyframeName} { ${keyframeRules} } @${keyframePrefix}keyframes ${keyframeName}-alt { ${keyframeRules} }`; styleContainer.appendChild(stylesheet); return stylesheet; }; const addClassToArray = (classes = [], className) => { if (className !== undefined) { const classNameToAppend = (Array.isArray(className)) ? className : [className]; return [...classes, ...classNameToAppend]; } return classes; }; const createAnimation = (animationId) => { let _delay; let _duration; let _easing; let _iterations; let _fill; let _direction; let _keyframes = []; let beforeAddClasses = []; let beforeRemoveClasses = []; let initialized = false; let parentAnimation; let beforeStylesValue = {}; let afterAddClasses = []; let afterRemoveClasses = []; let afterStylesValue = {}; let numAnimationsRunning = 0; let shouldForceLinearEasing = false; let shouldForceSyncPlayback = false; let cssAnimationsTimerFallback; let forceDirectionValue; let forceDurationValue; let forceDelayValue; let willComplete = true; let finished = false; let shouldCalculateNumAnimations = true; let keyframeName; let ani; const id = animationId; const onFinishCallbacks = []; const onFinishOneTimeCallbacks = []; const elements = []; const childAnimations = []; const stylesheets = []; const _beforeAddReadFunctions = []; const _beforeAddWriteFunctions = []; const _afterAddReadFunctions = []; const _afterAddWriteFunctions = []; const webAnimations = []; const supportsAnimationEffect = (typeof AnimationEffect === 'function' || typeof window.AnimationEffect === 'function'); const supportsWebAnimations = (typeof Element === 'function') && (typeof Element.prototype.animate === 'function') && supportsAnimationEffect; const ANIMATION_END_FALLBACK_PADDING_MS = 100; const getWebAnimations = () => { return webAnimations; }; const destroy = (clearStyleSheets) => { childAnimations.forEach(childAnimation => { childAnimation.destroy(clearStyleSheets); }); cleanUp(clearStyleSheets); elements.length = 0; childAnimations.length = 0; _keyframes.length = 0; clearOnFinish(); initialized = false; shouldCalculateNumAnimations = true; return ani; }; /** * Cancels any Web Animations, removes * any animation properties from the * animation's elements, and removes the * animation's stylesheets from the DOM. */ const cleanUp = (clearStyleSheets) => { cleanUpElements(); if (clearStyleSheets) { cleanUpStyleSheets(); } }; const resetFlags = () => { shouldForceLinearEasing = false; shouldForceSyncPlayback = false; shouldCalculateNumAnimations = true; forceDirectionValue = undefined; forceDurationValue = undefined; forceDelayValue = undefined; numAnimationsRunning = 0; finished = false; willComplete = true; }; const onFinish = (callback, opts) => { const callbacks = (opts && opts.oneTimeCallback) ? onFinishOneTimeCallbacks : onFinishCallbacks; callbacks.push({ c: callback, o: opts }); return ani; }; const clearOnFinish = () => { onFinishCallbacks.length = 0; onFinishOneTimeCallbacks.length = 0; return ani; }; /** * Cancels any Web Animations and removes * any animation properties from the * the animation's elements. */ const cleanUpElements = () => { if (supportsWebAnimations) { webAnimations.forEach(animation => { animation.cancel(); }); webAnimations.length = 0; } else { const elementsArray = elements.slice(); raf(() => { elementsArray.forEach(element => { removeStyleProperty(element, 'animation-name'); removeStyleProperty(element, 'animation-duration'); removeStyleProperty(element, 'animation-timing-function'); removeStyleProperty(element, 'animation-iteration-count'); removeStyleProperty(element, 'animation-delay'); removeStyleProperty(element, 'animation-play-state'); removeStyleProperty(element, 'animation-fill-mode'); removeStyleProperty(element, 'animation-direction'); }); }); } }; /** * Removes the animation's stylesheets * from the DOM. */ const cleanUpStyleSheets = () => { stylesheets.forEach(stylesheet => { /** * When sharing stylesheets, it's possible * for another animation to have already * cleaned up a particular stylesheet */ if (stylesheet && stylesheet.parentNode) { stylesheet.parentNode.removeChild(stylesheet); } }); stylesheets.length = 0; }; const beforeAddRead = (readFn) => { _beforeAddReadFunctions.push(readFn); return ani; }; const beforeAddWrite = (writeFn) => { _beforeAddWriteFunctions.push(writeFn); return ani; }; const afterAddRead = (readFn) => { _afterAddReadFunctions.push(readFn); return ani; }; const afterAddWrite = (writeFn) => { _afterAddWriteFunctions.push(writeFn); return ani; }; const beforeAddClass = (className) => { beforeAddClasses = addClassToArray(beforeAddClasses, className); return ani; }; const beforeRemoveClass = (className) => { beforeRemoveClasses = addClassToArray(beforeRemoveClasses, className); return ani; }; /** * Set CSS inline styles to the animation's * elements before the animation begins. */ const beforeStyles = (styles = {}) => { beforeStylesValue = styles; return ani; }; /** * Clear CSS inline styles from the animation's * elements before the animation begins. */ const beforeClearStyles = (propertyNames = []) => { for (const property of propertyNames) { beforeStylesValue[property] = ''; } return ani; }; const afterAddClass = (className) => { afterAddClasses = addClassToArray(afterAddClasses, className); return ani; }; const afterRemoveClass = (className) => { afterRemoveClasses = addClassToArray(afterRemoveClasses, className); return ani; }; const afterStyles = (styles = {}) => { afterStylesValue = styles; return ani; }; const afterClearStyles = (propertyNames = []) => { for (const property of propertyNames) { afterStylesValue[property] = ''; } return ani; }; const getFill = () => { if (_fill !== undefined) { return _fill; } if (parentAnimation) { return parentAnimation.getFill(); } return 'both'; }; const getDirection = () => { if (forceDirectionValue !== undefined) { return forceDirectionValue; } if (_direction !== undefined) { return _direction; } if (parentAnimation) { return parentAnimation.getDirection(); } return 'normal'; }; const getEasing = () => { if (shouldForceLinearEasing) { return 'linear'; } if (_easing !== undefined) { return _easing; } if (parentAnimation) { return parentAnimation.getEasing(); } return 'linear'; }; const getDuration = () => { if (shouldForceSyncPlayback) { return 0; } if (forceDurationValue !== undefined) { return forceDurationValue; } if (_duration !== undefined) { return _duration; } if (parentAnimation) { return parentAnimation.getDuration(); } return 0; }; const getIterations = () => { if (_iterations !== undefined) { return _iterations; } if (parentAnimation) { return parentAnimation.getIterations(); } return 1; }; const getDelay = () => { if (forceDelayValue !== undefined) { return forceDelayValue; } if (_delay !== undefined) { return _delay; } if (parentAnimation) { return parentAnimation.getDelay(); } return 0; }; const getKeyframes = () => { return _keyframes; }; const direction = (animationDirection) => { _direction = animationDirection; update(true); return ani; }; const fill = (animationFill) => { _fill = animationFill; update(true); return ani; }; const delay = (animationDelay) => { _delay = animationDelay; update(true); return ani; }; const easing = (animationEasing) => { _easing = animationEasing; update(true); return ani; }; const duration = (animationDuration) => { /** * CSS Animation Durations of 0ms work fine on Chrome * but do not run on Safari, so force it to 1ms to * get it to run on both platforms. */ if (!supportsWebAnimations && animationDuration === 0) { animationDuration = 1; } _duration = animationDuration; update(true); return ani; }; const iterations = (animationIterations) => { _iterations = animationIterations; update(true); return ani; }; const parent = (animation) => { parentAnimation = animation; return ani; }; const addElement = (el) => { if (el != null) { if (el.nodeType === 1) { elements.push(el); } else if (el.length >= 0) { for (let i = 0; i < el.length; i++) { elements.push(el[i]); } } else { console.error('Invalid addElement value'); } } return ani; }; const addAnimation = (animationToAdd) => { if (animationToAdd != null) { if (Array.isArray(animationToAdd)) { for (const animation of animationToAdd) { animation.parent(ani); childAnimations.push(animation); } } else { animationToAdd.parent(ani); childAnimations.push(animationToAdd); } } return ani; }; const keyframes = (keyframeValues) => { _keyframes = keyframeValues; return ani; }; /** * Run all "before" animation hooks. */ const beforeAnimation = () => { // Runs all before read callbacks _beforeAddReadFunctions.forEach(callback => callback()); // Runs all before write callbacks _beforeAddWriteFunctions.forEach(callback => callback()); // Updates styles and classes before animation runs const addClasses = beforeAddClasses; const removeClasses = beforeRemoveClasses; const styles = beforeStylesValue; elements.forEach(el => { const elementClassList = el.classList; addClasses.forEach(c => elementClassList.add(c)); removeClasses.forEach(c => elementClassList.remove(c)); for (const property in styles) { if (styles.hasOwnProperty(property)) { setStyleProperty(el, property, styles[property]); } } }); }; /** * Run all "after" animation hooks. */ const afterAnimation = () => { clearCSSAnimationsTimeout(); // Runs all after read callbacks _afterAddReadFunctions.forEach(callback => callback()); // Runs all after write callbacks _afterAddWriteFunctions.forEach(callback => callback()); // Updates styles and classes before animation ends const currentStep = willComplete ? 1 : 0; const addClasses = afterAddClasses; const removeClasses = afterRemoveClasses; const styles = afterStylesValue; elements.forEach(el => { const elementClassList = el.classList; addClasses.forEach(c => elementClassList.add(c)); removeClasses.forEach(c => elementClassList.remove(c)); for (const property in styles) { if (styles.hasOwnProperty(property)) { setStyleProperty(el, property, styles[property]); } } }); onFinishCallbacks.forEach(onFinishCallback => { return onFinishCallback.c(currentStep, ani); }); onFinishOneTimeCallbacks.forEach(onFinishCallback => { return onFinishCallback.c(currentStep, ani); }); onFinishOneTimeCallbacks.length = 0; shouldCalculateNumAnimations = true; if (willComplete) { finished = true; } willComplete = true; }; const animationFinish = () => { if (numAnimationsRunning === 0) { return; } numAnimationsRunning--; if (numAnimationsRunning === 0) { afterAnimation(); if (parentAnimation) { parentAnimation.animationFinish(); } } }; const initializeCSSAnimation = (toggleAnimationName = true) => { cleanUpStyleSheets(); const processedKeyframes = processKeyframes(_keyframes); elements.forEach(element => { if (processedKeyframes.length > 0) { const keyframeRules = generateKeyframeRules(processedKeyframes); keyframeName = (animationId !== undefined) ? animationId : generateKeyframeName(keyframeRules); const stylesheet = createKeyframeStylesheet(keyframeName, keyframeRules, element); stylesheets.push(stylesheet); setStyleProperty(element, 'animation-duration', `${getDuration()}ms`); setStyleProperty(element, 'animation-timing-function', getEasing()); setStyleProperty(element, 'animation-delay', `${getDelay()}ms`); setStyleProperty(element, 'animation-fill-mode', getFill()); setStyleProperty(element, 'animation-direction', getDirection()); const iterationsCount = (getIterations() === Infinity) ? 'infinite' : getIterations().toString(); setStyleProperty(element, 'animation-iteration-count', iterationsCount); setStyleProperty(element, 'animation-play-state', 'paused'); if (toggleAnimationName) { setStyleProperty(element, 'animation-name', `${stylesheet.id}-alt`); } raf(() => { setStyleProperty(element, 'animation-name', stylesheet.id || null); }); } }); }; const initializeWebAnimation = () => { elements.forEach(element => { const animation = element.animate(_keyframes, { id, delay: getDelay(), duration: getDuration(), easing: getEasing(), iterations: getIterations(), fill: getFill(), direction: getDirection() }); animation.pause(); webAnimations.push(animation); }); if (webAnimations.length > 0) { webAnimations[0].onfinish = () => { animationFinish(); }; } }; const initializeAnimation = (toggleAnimationName = true) => { beforeAnimation(); if (_keyframes.length > 0) { if (supportsWebAnimations) { initializeWebAnimation(); } else { initializeCSSAnimation(toggleAnimationName); } } initialized = true; }; const setAnimationStep = (step) => { step = Math.min(Math.max(step, 0), 0.9999); if (supportsWebAnimations) { webAnimations.forEach(animation => { animation.currentTime = animation.effect.getComputedTiming().delay + (getDuration() * step); animation.pause(); }); } else { const animationDuration = `-${getDuration() * step}ms`; elements.forEach(element => { if (_keyframes.length > 0) { setStyleProperty(element, 'animation-delay', animationDuration); setStyleProperty(element, 'animation-play-state', 'paused'); } }); } }; const updateWebAnimation = (step) => { webAnimations.forEach(animation => { animation.effect.updateTiming({ delay: getDelay(), duration: getDuration(), easing: getEasing(), iterations: getIterations(), fill: getFill(), direction: getDirection() }); }); if (step !== undefined) { setAnimationStep(step); } }; const updateCSSAnimation = (toggleAnimationName = true, step) => { raf(() => { elements.forEach(element => { setStyleProperty(element, 'animation-name', keyframeName || null); setStyleProperty(element, 'animation-duration', `${getDuration()}ms`); setStyleProperty(element, 'animation-timing-function', getEasing()); setStyleProperty(element, 'animation-delay', (step !== undefined) ? `-${step * getDuration()}ms` : `${getDelay()}ms`); setStyleProperty(element, 'animation-fill-mode', getFill() || null); setStyleProperty(element, 'animation-direction', getDirection() || null); const iterationsCount = (getIterations() === Infinity) ? 'infinite' : getIterations().toString(); setStyleProperty(element, 'animation-iteration-count', iterationsCount); if (toggleAnimationName) { setStyleProperty(element, 'animation-name', `${keyframeName}-alt`); } raf(() => { setStyleProperty(element, 'animation-name', keyframeName || null); }); }); }); }; const update = (deep = false, toggleAnimationName = true, step) => { if (deep) { childAnimations.forEach(animation => { animation.update(deep, toggleAnimationName, step); }); } if (supportsWebAnimations) { updateWebAnimation(step); } else { updateCSSAnimation(toggleAnimationName, step); } return ani; }; const progressStart = (forceLinearEasing = false, step) => { childAnimations.forEach(animation => { animation.progressStart(forceLinearEasing, step); }); pauseAnimation(); shouldForceLinearEasing = forceLinearEasing; if (!initialized) { initializeAnimation(); } else { update(false, true, step); } return ani; }; const progressStep = (step) => { childAnimations.forEach(animation => { animation.progressStep(step); }); setAnimationStep(step); return ani; }; const progressEnd = (playTo, step, dur) => { shouldForceLinearEasing = false; childAnimations.forEach(animation => { animation.progressEnd(playTo, step, dur); }); if (dur !== undefined) { forceDurationValue = dur; } finished = false; // tslint:disable-next-line: strict-boolean-conditions willComplete = true; if (playTo === 0) { forceDirectionValue = (getDirection() === 'reverse') ? 'normal' : 'reverse'; if (forceDirectionValue === 'reverse') { willComplete = false; } if (supportsWebAnimations) { update(); setAnimationStep(1 - step); } else { forceDelayValue = ((1 - step) * getDuration()) * -1; update(false, false); } } else if (playTo === 1) { if (supportsWebAnimations) { update(); setAnimationStep(step); } else { forceDelayValue = (step * getDuration()) * -1; update(false, false); } } if (playTo !== undefined) { onFinish(() => { forceDurationValue = undefined; forceDirectionValue = undefined; forceDelayValue = undefined; }, { oneTimeCallback: true }); if (!parentAnimation) { play(); } } return ani; }; const pauseAnimation = () => { if (initialized) { if (supportsWebAnimations) { webAnimations.forEach(animation => { animation.pause(); }); } else { elements.forEach(element => { setStyleProperty(element, 'animation-play-state', 'paused'); }); } } }; const pause = () => { childAnimations.forEach(animation => { animation.pause(); }); pauseAnimation(); return ani; }; const onAnimationEndFallback = () => { cssAnimationsTimerFallback = undefined; animationFinish(); }; const clearCSSAnimationsTimeout = () => { if (cssAnimationsTimerFallback) { clearTimeout(cssAnimationsTimerFallback); } }; const playCSSAnimations = () => { clearCSSAnimationsTimeout(); raf(() => { elements.forEach(element => { if (_keyframes.length > 0) { setStyleProperty(element, 'animation-play-state', 'running'); } }); }); if (_keyframes.length === 0 || elements.length === 0) { animationFinish(); } else { /** * This is a catchall in the event that a CSS Animation did not finish. * The Web Animations API has mechanisms in place for preventing this. * CSS Animations will not fire an `animationend` event * for elements with `display: none`. The Web Animations API * accounts for this, but using raw CSS Animations requires * this workaround. */ const animationDelay = getDelay() || 0; const animationDuration = getDuration() || 0; const animationIterations = getIterations() || 1; // No need to set a timeout when animation has infinite iterations if (isFinite(animationIterations)) { cssAnimationsTimerFallback = setTimeout(onAnimationEndFallback, animationDelay + (animationDuration * animationIterations) + ANIMATION_END_FALLBACK_PADDING_MS); } animationEnd(elements[0], () => { clearCSSAnimationsTimeout(); /** * Ensure that clean up * is always done a frame * before the onFinish handlers * are fired. Otherwise, there * may be flickering if a new * animation is started on the same * element too quickly * * TODO: Is there a cleaner way to do this? */ raf(() => { clearCSSAnimationPlayState(); raf(animationFinish); }); }); } }; const clearCSSAnimationPlayState = () => { elements.forEach(element => { removeStyleProperty(element, 'animation-duration'); removeStyleProperty(element, 'animation-delay'); removeStyleProperty(element, 'animation-play-state'); }); }; const playWebAnimations = () => { webAnimations.forEach(animation => { animation.play(); }); if (_keyframes.length === 0 || elements.length === 0) { animationFinish(); } }; const resetAnimation = () => { if (supportsWebAnimations) { setAnimationStep(0); updateWebAnimation(); } else { updateCSSAnimation(); } }; const play = (opts) => { return new Promise(resolve => { if (opts && opts.sync) { shouldForceSyncPlayback = true; onFinish(() => shouldForceSyncPlayback = false, { oneTimeCallback: true }); } if (!initialized) { initializeAnimation(); } if (finished) { resetAnimation(); finished = false; } if (shouldCalculateNumAnimations) { numAnimationsRunning = childAnimations.length + 1; shouldCalculateNumAnimations = false; } onFinish(() => resolve(), { oneTimeCallback: true }); childAnimations.forEach(animation => { animation.play(); }); if (supportsWebAnimations) { playWebAnimations(); } else { playCSSAnimations(); } }); }; const stop = () => { childAnimations.forEach(animation => { animation.stop(); }); if (initialized) { cleanUpElements(); initialized = false; } resetFlags(); }; const from = (property, value) => { const firstFrame = _keyframes[0]; if (firstFrame !== undefined && (firstFrame.offset === undefined || firstFrame.offset === 0)) { firstFrame[property] = value; } else { _keyframes = [ { offset: 0, [property]: value }, ..._keyframes ]; } return ani; }; const to = (property, value) => { const lastFrame = _keyframes[_keyframes.length - 1]; if (lastFrame !== undefined && (lastFrame.offset === undefined || lastFrame.offset === 1)) { lastFrame[property] = value; } else { _keyframes = [ ..._keyframes, { offset: 1, [property]: value } ]; } return ani; }; const fromTo = (property, fromValue, toValue) => { return from(property, fromValue).to(property, toValue); }; return ani = { parentAnimation, elements, childAnimations, id, animationFinish, from, to, fromTo, parent, play, pause, stop, destroy, keyframes, addAnimation, addElement, update, fill, direction, iterations, duration, easing, delay, getWebAnimations, getKeyframes, getFill, getDirection, getDelay, getIterations, getEasing, getDuration, afterAddRead, afterAddWrite, afterClearStyles, afterStyles, afterRemoveClass, afterAddClass, beforeAddRead, beforeAddWrite, beforeClearStyles, beforeStyles, beforeRemoveClass, beforeAddClass, onFinish, progressStart, progressStep, progressEnd }; }; export { createAnimation as c };