export const createAnimationController = (animations, component) => { return { animations, start: () => { Object.entries(animations).map(([name, animation]) => animation.run((state) => { component.setState({ ...component.state, [name]: state, }); })); }, prepare: () => { Object.entries(animations).map(([name, animation]) => animation.prepare((state) => { component.setState({ ...component.state, [name]: state, }); })); } }; } export const createAnimation = (stateA, stateB, delay) => ({ stateA, stateB, delay, }); const getAnimationState = (animation, stateName = 'stateA') => { if (animation instanceof Array) { return animation.map(a => getAnimationState(a, stateName)); } return animation[stateName]; }; const getMaxTransitionTime = (transition) => { const re = /(\d+)ms/g; const times = []; let matches; while ((matches = re.exec(transition)) != null) { times.push(parseInt(matches[1], 10)); } return Math.max.apply(null, times); }; const getAnimationDuration = (animation) => { if (animation instanceof Array) { return animation.reduce((maxDuration, a) => { const duration = getAnimationDuration(a); if (duration > maxDuration) { return duration; } return maxDuration; }, 0); } const state = animation.stateB; const maxTransition = state.transition ? getMaxTransitionTime(state.transition) : 0; return maxTransition + animation.delay; }; const getMaxAnimationsDuration = (animations) => ( getAnimationDuration(Object.values(animations)) ); export const createAnimationRunner = (animations, subscriber) => { let state = Object.entries(animations).reduce((state, [name, animation]) => ({ ...state, [name]: getAnimationState(animation), }), {}); const animationsDuration = getMaxAnimationsDuration(animations); const animate = (name, animation, stateName, getDelay) => { if (animation instanceof Array) { animation.map((a, index) => { window.requestAnimationFrame(() => { window.setTimeout(() => { state = { ...state, [name]: [ ...(state[name].slice(0, index)), a[stateName], ...(state[name].slice(index + 1, state[name].length)), ], }; subscriber(); }, getDelay(a)) }); }); } else { window.requestAnimationFrame(() => { window.setTimeout(() => { state = { ...state, [name]: animation[stateName], }; subscriber(); }, getDelay(animation)) }); } } return { getState() { return state; }, run() { Object.entries(animations).forEach(([name, animation]) => { animate(name, animation, 'stateB', a => a.delay) }) }, runReverse() { Object.entries(animations).reverse().forEach(([name, animation]) => { animate(name, animation, 'stateA', a => animationsDuration - a.delay) }) }, awaitAnimationComplete(callback) { window.setTimeout(callback, animationsDuration); }, } } // prepare(callback) { // callback(stateA); // }, // run(callback) { // window,requestAnimationFrame(() => { // window.setTimeout(() => { // callback(stateB); // }, delay) // }); // },