#include easings.js // This probably need a bit of a rewrite at some point to allow starting for arbitrary time points const MamiAnimate = info => { if(typeof info !== 'object') throw 'info must be an object'; let onUpdate; if('update' in info && typeof info.update === 'function') onUpdate = info.update; if(onUpdate === undefined) throw 'update is a required parameter for info'; let duration; if('duration' in info) duration = parseFloat(info.duration); if(duration <= 0) throw 'duration is a required parameter for info and must be greater than 0'; let onStart; if('start' in info && typeof info.start === 'function') onStart = info.start; let onEnd; if('end' in info && typeof info.end === 'function') onEnd = info.end; let onCancel; if('cancel' in info && typeof info.cancel === 'function') onCancel = info.cancel; let easing; if('easing' in info) easing = info.easing; let delayed; if('delayed' in info) delayed = !!info.delayed; let async; if('async' in info) async = !!info.async; const easingType = typeof easing; if(easingType !== 'function') { if(easingType === 'string' && easing in MamiEasings) easing = MamiEasings[easing]; else easing = MamiEasings.linear; } let tStart, tLast, cancel = false, tRawCompletion = 0, tCompletion = 0, started = !delayed; const update = tCurrent => { if(tStart === undefined) { tStart = tCurrent; if(onStart !== undefined) onStart(); } const tElapsed = tCurrent - tStart; tRawCompletion = Math.min(1, Math.max(0, tElapsed / duration)); tCompletion = easing(tRawCompletion); onUpdate(tCompletion, tRawCompletion); if(tElapsed < duration) { if(cancel) { if(onCancel !== undefined) onCancel(tCompletion, tRawCompletion); return; } tLast = tCurrent; requestAnimationFrame(update); } else if(onEnd !== undefined) onEnd(); }; let promise; if(async) promise = new Promise((resolve, reject) => { if(onCancel === undefined) { onCancel = reject; } else { const realOnCancel = onCancel; onCancel = (...args) => { realOnCancel(...args); reject(...args); }; } if(onEnd === undefined) { onEnd = resolve; } else { const realOnEnd = onEnd; onEnd = (...args) => { realOnEnd(...args); resolve(...args); }; } if(!delayed) requestAnimationFrame(update); }); if(!delayed) { if(promise !== undefined) return promise; requestAnimationFrame(update); } return { getCompletion: () => tCompletion, getRawCompletion: () => tRawCompletion, start: () => { if(!started) { started = true; requestAnimationFrame(update); } if(promise !== undefined) return promise; }, cancel: () => { cancel = true; }, }; };