/* oktransition.add({ obj: el.style, units: "px", from: { left: 0 }, to: { left: 100 }, duration: 1000, easing: oktransition.easing.circ_out, update: function(obj){ console.log(obj.left) } finished: function(){ console.log("done") } }) */ const oktransition = {}; let transitions = []; let last_t = 0; let id = 0; const lerp = (n, a, b) => (b - a) * n + a; oktransition.speed = 1; oktransition.add = (transition) => { transition.id = id++; transition.obj = transition.obj || {}; if (transition.easing) { if (typeof transition.easing === "string") { transition.easing = oktransition.easing[transition.easing]; } } else { transition.easing = oktransition.easing.linear; } if (!("from" in transition) && !("to" in transition)) { transition.keys = []; } else if (!("from" in transition)) { transition.from = {}; transition.keys = Object.keys(transition.to); transition.keys.forEach(function (prop) { transition.from[prop] = parseFloat(transition.obj[prop]); }); } else { transition.keys = Object.keys(transition.from); } transition.delay = transition.delay || 0; transition.start = last_t + transition.delay; transition.done = false; transition.after = transition.after || []; transition.then = (fn) => { transition.after.push(fn); return transition; }; transition.tick = 0; transition.skip = transition.skip || 1; transition.dt = 0; transition.cancel = () => (transitions = transitions.filter((item) => item !== transition)); transitions.push(transition); return transition; }; oktransition.update = (t) => { let done = false; requestAnimationFrame(oktransition.update); last_t = t * oktransition.speed; if (transitions.length === 0) return; transitions.forEach((transition, i) => { const dt = Math.min(1.0, (t - transition.start) / transition.duration); transition.tick++; if ( dt < 0 || (dt < 1 && transition.tick % transition.skip != 0) || transition.done ) return; const ddt = transition.easing(dt); transition.dt = ddt; transition.keys.forEach((prop) => { let val = lerp(ddt, transition.from[prop], transition.to[prop]); if (transition.round) val = Math.round(val); if (transition.units) val = Math.round(val) + transition.units; transition.obj[prop] = val; }); if (transition.update) { transition.update(transition.obj, dt); } if (dt === 1) { if (transition.finished) { transition.finished(transition); } if (transition.after.length) { const twn = transition.after.shift(); twn.obj = twn.obj || transition.obj; twn.after = transition.after; oktransition.add(twn); } if (transition.loop) { transition.start = t + transition.delay; } else { done = true; transition.done = true; } } }); if (done) { transitions = transitions.filter((transition) => !transition.done); } }; requestAnimationFrame(oktransition.update); oktransition.easing = { linear: (t) => { return t; }, circ_out: (t) => { return Math.sqrt(1 - (t = t - 1) * t); }, circ_in: (t) => { return -(Math.sqrt(1 - t * t) - 1); }, circ_in_out: (t) => { return (t *= 2) < 1 ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); }, quad_in: (n) => { return Math.pow(n, 2); }, quad_out: (n) => { return n * (n - 2) * -1; }, quad_in_out: (n) => { n = n * 2; if (n < 1) { return Math.pow(n, 2) / 2; } return (-1 * (--n * (n - 2) - 1)) / 2; }, cubic_bezier: (mX1, mY1, mX2, mY2) => { function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } function C(aA1) { return 3.0 * aA1; } // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. function CalcBezier(aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; } // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. function GetSlope(aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); } function GetTForX(aX) { // Newton raphson iteration let aGuessT = aX; for (let i = 0; i < 10; ++i) { const currentSlope = GetSlope(aGuessT, mX1, mX2); if (currentSlope == 0.0) return aGuessT; const currentX = CalcBezier(aGuessT, mX1, mX2) - aX; aGuessT -= currentX / currentSlope; } return aGuessT; } return function (aX) { if (mX1 == mY1 && mX2 == mY2) return aX; // linear return CalcBezier(aX, mY1, mY2); }; }, }; export default oktransition;