local pi = math.pi local sin = math.sin local cos = math.cos local abs = math.abs local function backIn(t, s) s = s or 1.70158; return t * t * ((s + 1) * t - s) end local function backOut(t, s) s = s or 1.70158; t = t - 1; return 1 + t * t * ((s + 1) * t + s) end local function backInOut(t, s) s = s or 1.70158 if t < 0.5 then local tt = 2 * t return (tt * tt * ((s * 1.525 + 1) * tt - s * 1.525)) / 2 else local tt = 2 * t - 2 return (2 + tt * tt * ((s * 1.525 + 1) * tt + s * 1.525)) / 2 end end -- Bounce (helper) local function bounceOut(t) local n1 = 7.5625 local d1 = 2.75 if t < 1 / d1 then return n1 * t * t elseif t < 2 / d1 then t = t - 1.5 / d1 return n1 * t * t + 0.75 elseif t < 2.5 / d1 then t = t - 2.25 / d1 return n1 * t * t + 0.9375 else t = t - 2.625 / d1 return n1 * t * t + 0.984375 end end easing = { linear = function(t) return t end, -- Sine easeInSine = function(t) return 1 - cos((t * pi) / 2) end, easeOutSine = function(t) return sin((t * pi) / 2) end, easeInOutSine = function(t) return -(cos(pi * t) - 1) / 2 end, -- Quad easeInQuad = function(t) return t * t end, easeOutQuad = function(t) return 1 - (1 - t) * (1 - t) end, easeInOutQuad = function(t) if t < 0.5 then return 2 * t * t end return 1 - ((-2 * t + 2)^2) / 2 end, -- Cubic easeInCubic = function(t) return t * t * t end, easeOutCubic = function(t) return 1 - ((1 - t)^3) end, easeInOutCubic = function(t) if t < 0.5 then return 4 * t * t * t end return 1 - ((-2 * t + 2)^3) / 2 end, -- Expo easeInExpo = function(t) return (t == 0) and 0 or (2^(10 * t - 10)) end, easeOutExpo = function(t) return (t == 1) and 1 or 1 - (2^(-10 * t)) end, easeInOutExpo = function(t) if t == 0 then return 0 end if t == 1 then return 1 end if t < 0.5 then return (2^(20 * t - 10)) / 2 end return (2 - (2^(-20 * t + 10))) / 2 end, -- Back (overshoot) easeInBack = function(t) return backIn(t) end, easeOutBack = function(t) return backOut(t) end, easeInOutBack = function(t) return backInOut(t) end, -- Elastic easeInElastic = function(t) if t == 0 then return 0 end if t == 1 then return 1 end local c4 = (2 * pi) / 3 return -(2^(10 * t - 10)) * sin((t * 10 - 10.75) * c4) end, easeOutElastic = function(t) if t == 0 then return 0 end if t == 1 then return 1 end local c4 = (2 * pi) / 3 return (2^(-10 * t)) * sin((t * 10 - 0.75) * c4) + 1 end, easeInOutElastic = function(t) if t == 0 then return 0 end if t == 1 then return 1 end local c5 = (2 * pi) / 4.5 if t < 0.5 then return -((2^(20 * t - 10)) * sin((20 * t - 11.125) * c5)) / 2 end return ((2^(-20 * t + 10)) * sin((20 * t - 11.125) * c5)) / 2 + 1 end, -- Bounce easeInBounce = function(t) return 1 - bounceOut(1 - t) end, easeOutBounce = function(t) return bounceOut(t) end, easeInOutBounce = function(t) if t < 0.5 then return (1 - bounceOut(1 - 2 * t)) / 2 end return (1 + bounceOut(2 * t - 1)) / 2 end, } tweening = { list = {}, add = function(from, to, duration, easingFun, callback) local tween = { from = from, to = to, duration = math.max(0.000001, duration or 0.001), easing = easingFun, callback = callback, t = 0, } tweening.list[#tweening.list + 1] = tween return tween end, update = function(dt) if #tweening.list==0 then return end -- iterate backwards so we can remove finished tweens safely for i = #tweening.list, 1, -1 do local tw = tweening.list[i] tw.t = tw.t + (dt or 0) local progress = tw.t / tw.duration if progress >= 1 then -- finished: ensure final value, call callback with finished=true, remove local value = tw.to if tw.callback then -- callback(value, normalizedProgress, finished) tw.callback(value, 1, true) end table.remove(tweening.list, i) else -- in progress local alpha = tw.easing(progress) local value = tw.from + (tw.to - tw.from) * alpha if tw.callback then tw.callback(value, progress, false) end end end end, clear = function() for i = #tweening.list, 1, -1 do table.remove(tweening.list, i) end end, -- Optional helper: cancel a specific tween (pass the tween returned by add) cancel = function(tween) for i = #tweening.list, 1, -1 do if tweening.list[i] == tween then table.remove(tweening.list, i) return true end end return false end }