Require('Math') local math = math local math_round = _G.math.round or (function(number) return (math_floor(number + 0.5)) end) AnimLib = { threads = {}, lerps = {}, } setmetatable(AnimLib, { __index = AnimLib, __call = function(self, ...) local name = select(1, ...) if type (name) == 'table' then if name.name or name.Name then name = name.name or name.Name values = select(1, ...) values.name = nil values.Name = nil else return error('name is required') end else assert(name, 'name is required') values = select(2, ...) assert(values, 'Values is required') assert(type(values) == 'table', 'Values must be a table') end if self:Exists(name) then return self:Edit(name, values) else self:Create(name, values) return self:Edit(name, values) end end }) function AnimLib:UpdateThread(thread, sliceTime, name) if thread and thread.running then if sliceTime then if thread.timeElapsed == nil then thread.timeElapsed = 0 end thread.timeElapsed = thread.timeElapsed + sliceTime local elapsedTime = thread.timeElapsed local duration = thread.endTime - thread.startTime local progress = elapsedTime / duration if progress >= 1 then thread.current = thread.target thread.running = false thread.isFinished = true if thread.onComplete and type(thread.onComplete) == "function" then thread.onComplete(thread.current) end thread.timeElapsed = 0 else local scaledProgress = self:Interpolate(thread.animType, progress) thread.current = thread.startValue + scaledProgress * (thread.target - thread.startValue) if thread.forceInt then thread.current = math_round(thread.current) end if thread.onProgress and type(thread.onProgress) == "function" then thread.onProgress(thread.current, progress) end if thread.variableHook and type(thread.variableHook) == "function" then thread.variableHook(thread.current) end end else local now = getTickCount() if now >= thread.startTime then local elapsedTime = now - thread.startTime local duration = thread.endTime - thread.startTime local progress = elapsedTime / duration if progress >= 1 then thread.current = thread.target thread.running = false thread.isFinished = true if thread.onComplete and type(thread.onComplete) == 'function' then thread.onComplete(thread.current) end else local scaledProgress = self:Interpolate(thread.animType, progress) thread.current = thread.startValue + scaledProgress * (thread.target - thread.startValue) if thread.forceInt then thread.current = math_round(thread.current) end if thread.onProgress and type(thread.onProgress) == 'function' then thread.onProgress(thread.current, progress) end if thread.variableHook and type(thread.variableHook) == 'function' then thread.variableHook(thread.current) end end end end end end function AnimLib:_EOB(x) local a = 7.5625 local b = 2.75 if x then if x < 1 / b then return (a * x * x) elseif x < 2 / b then x = x - 1.5 / b return (a * x * x + 0.75) elseif x < 2.5 / b then x = x - 2.25 / b return (a * x * x + 0.9375) else x = x - 2.625 / b return (a * x * x + 0.984375) end end end function AnimLib:Interpolate(type, v) if (not type) then type = 'Linear' end return (type == 'Linear' and v) or (type == 'InSine' and 1 - math.cos((v * math.pi) / 2)) or (type == 'OutSine' and math.sin((v * math.pi) / 2)) or (type == 'InOutSine' and -(math.cos(math.pi * v) - 1) / 2) or (type == 'InQuad' and v * v) or (type == 'OutQuad' and v * (2 - v)) or (type == 'InOutQuad' and (v < 0.5 and 2 * v * v or -1 + (4 - 2 * v) * v)) or (type == 'InCubic' and v * v * v) or (type == 'OutCubic' and (v - 1) * (v - 1) * (v - 1) + 1) or (type == 'InOutCubic' and (v < 0.5 and 4 * v * v * v or 0.5 * (2 * v - 2) * (2 * v - 2) * (2 * v - 2) + 1)) or (type == 'InQuart' and v * v * v * v) or (type == 'OutQuart' and (v - 1) * (v - 1) * (v - 1) * (1 - v) + 1) or (type == 'InOutQuart' and (v < 0.5 and 8 * v * v * v * v or -0.5 * (v - 1) * (v - 1) * (v - 1) * (v - 1) + 1)) or (type == 'InQuint' and v * v * v * v * v) or (type == 'OutQuint' and (v - 1) * (v - 1) * (v - 1) * (v - 1) * (v - 1) + 1) or (type == 'InOutQuint' and (v < 0.5 and 16 * v * v * v * v * v or 0.5 * (2 * v - 2) * (2 * v - 2) * (2 * v - 2) * (2 * v - 2) * (2 * v - 2) + 1)) or (type == 'InExpo' and (v == 0 and 0 or 2 ^ (10 * (v - 1)))) or (type == 'OutExpo' and (v == 1 and 1 or 1 - 2 ^ (-10 * v))) or (type == 'InOutExpo' and (v == 0 and 0 or v == 1 and 1 or (v < 0.5 and 2 ^ (20 * v - 10) * 0.5 or (2 - 2 ^ (-20 * v + 10)) * 0.5))) or (type == 'InCirc' and -(math.sqrt(1 - v * v) - 1)) or (type == 'OutCirc' and math.sqrt(1 - (v - 1) * (v - 1))) or (type == 'OutElastic' and (v == 0 and 0 or v == 1 and 1 or 2 ^ (-10 * v) * math.sin((v * 10 - 0.75) * ((2 * math.pi) / 3)) + 1)) or (type == 'InBounce' and 1 - self:_EOB(1 - v)) or (type == 'OutBounce' and self:_EOB(v)) or (type == 'InOutBounce' and (v < 0.5 and (1 - self:_EOB(1 - 2 * v)) * 0.5 or (self:_EOB(2 * v - 1) + 1) * 0.5)) or -- NEW ANIMATIONS (type == 'OutSpring' and (v == 1 and 1 or 1 + (2 ^ (-10 * v) * math.sin((v * 10 - 0.3) * (2 * math.pi) / 0.4)))) or (type == 'InOutWave' and (math.sin(v * math.pi * 2) * 0.5 + 0.5)) or (type == 'InOutBack' and (v < 0.5 and (2 * v)^2 * ((1.7 + 1) * 2 * v - 1.7) / 2 or ((2 * v - 2)^2 * ((1.7 + 1) * (v * 2 - 2) + 1.7) + 2) / 2)) or (type == 'InBack' and v * v * ((1.70158 + 1) * v - 1.70158)) or (type == 'OutBack' and (v - 1) * (v - 1) * ((1.70158 + 1) * (v - 1) + 1.70158) + 1) or (type == 'OutZigzag' and (v + math.sin(v * math.pi * 4) * 0.1)) or (type == 'InOutPulse' and (math.sin(v * math.pi * 10) * 0.05 + v)) or (type == 'InOutRandomBounce' and (v < 0.5 and (2 * v)^2 * math.random() / 2 or 1 - ((1 - v) * 2)^2 * math.random() / 2)) or (type == 'OutGravityFall' and (v * v * (3 - 2 * v))) or (type == 'InOutSpiral' and (math.sin(v * math.pi) * math.cos(v * math.pi) * v)) or (type == 'Smoothstep' and (v * v * (3 - 2 * v))) or (type == 'Spring' and (1 - math.exp(-6 * v) * (math.cos(12 * v) + math.sin(12 * v) / 2))) or (type == 'ExponentialEaseOut' and (1 - 2 ^ (-10 * v))) or (type == 'LogEaseIn' and math.log(1 + 9 * v) / math.log(10)) or (type == 'LogEaseOut' and 1 - math.log(1 + 9 * (1 - v)) / math.log(10)) or (type == 'LogEaseInOut' and (v < 0.5 and math.log(1 + 9 * v) / math.log(10) / 2 or 1 - math.log(1 + 9 * (1 - v)) / math.log(10) / 2)) or (type == 'TriangleWaveAnimation' and 2 * math.abs(0.5 - (v % 1))) or (type == 'SquareWaveAnimation' and (v % 1 < 0.5 and 0 or 1)) or (type == 'SawtoothWaveAnimation' and (v % 1)) or (type == 'InverseSawtoothWaveAnimation' and 1 - (v % 1)) or (type == 'PulseWaveAnimation' and (v % 1 < 0.5 and 1 or 0)) or (type == 'InversePulseWaveAnimation' and (v % 1 < 0.5 and 0 or 1)) or (type == 'SmoothWaveAnimation' and (1 - math.cos(v * math.pi * 2)) / 2) or (type == 'InverseSmoothWaveAnimation' and (1 + math.cos(v * math.pi * 2)) / 2) or (type == 'OvershootEase' and v * v * ((1.2 + 1) * v - 1.2)) or (type == 'WeakWobble' and v + 0.06 * math.sin(4 * v * math.pi)) or (type == 'Wobble' and v + 0.15 * math.sin(4 * v * math.pi)) or (type == 'StrongWobble' and v + 0.3 * math.sin(4 * v * math.pi)) or (type == 'SoftBounce' and 1 - math.cos(v * math.pi * 2) * math.exp(-v * 6)) or (type == 'MediumBounce' and 1 - math.cos(v * math.pi * 2) * math.exp(-v * 9)) or (type == 'HardBounce' and 1 - math.cos(v * math.pi * 2) * math.exp(-v * 12)) or (type == 'SoftSpring' and 1 - math.exp(-v * 6) * math.cos(v * math.pi * 2)) or (type == 'MediumSpring' and 1 - math.exp(-v * 9) * math.cos(v * math.pi * 2)) or (type == 'HardSpring' and 1 - math.exp(-v * 12) * math.cos(v * math.pi * 2)) or (type == 'SoftElastic' and 1 - math.exp(-v * 6) * math.cos(v * math.pi * 2) * math.exp(-v * 6)) or (type == 'MediumElastic' and 1 - math.exp(-v * 9) * math.cos(v * math.pi * 2) * math.exp(-v * 9)) or (type == 'HardElastic' and 1 - math.exp(-v * 12) * math.cos(v * math.pi * 2) * math.exp(-v * 12)) or (type == 'SoftBounceBack' and 1 - math.cos(v * math.pi * 2) * math.exp(-v * 6) * math.cos(v * math.pi * 2)) or (type == 'MediumBounceBack' and 1 - math.cos(v * math.pi * 2) * math.exp(-v * 9) * math.cos(v * math.pi * 2)) or (type == 'HardBounceBack' and 1 - math.cos(v * math.pi * 2) * math.exp(-v * 12) * math.cos(v * math.pi * 2)) or (type == 'SoftSpringBack' and 1 - math.exp(-v * 6) * math.cos(v * math.pi * 2) * math.exp(-v * 6) * math.cos(v * math.pi * 2)) or (type == 'MediumSpringBack' and 1 - math.exp(-v * 9) * math.cos(v * math.pi * 2) * math.exp(-v * 9) * math.cos(v * math.pi * 2)) or (type == 'HardSpringBack' and 1 - math.exp(-v * 12) * math.cos(v * math.pi * 2) * math.exp(-v * 12) * math.cos(v * math.pi * 2)) or (type == 'SoftElasticBack' and 1 - math.exp(-v * 6) * math.cos(v * math.pi * 2) * math.exp(-v * 6) * math.cos(v * math.pi * 2) * math.exp(-v * 6)) or (type == 'MediumElasticBack' and 1 - math.exp(-v * 9) * math.cos(v * math.pi * 2) * math.exp(-v * 9) * math.cos(v * math.pi * 2) * math.exp(-v * 9)) or (type == 'HardElasticBack' and 1 - math.exp(-v * 12) * math.cos(v * math.pi * 2) * math.exp(-v * 12) * math.cos(v * math.pi * 2) * math.exp(-v * 12)) or (type == 'SoftBounceInOut' and (v < 0.5 and 1 - math.cos(v * math.pi * 2) * math.exp(-v * 6) or math.cos(v * math.pi * 2) * math.exp(-v * 6))) or (type == 'MediumBounceInOut' and (v < 0.5 and 1 - math.cos(v * math.pi * 2) * math.exp(-v * 9) or math.cos(v * math.pi * 2) * math.exp(-v * 9))) or (type == 'HardBounceInOut' and (v < 0.5 and 1 - math.cos(v * math.pi * 2) * math.exp(-v * 12) or math.cos(v * math.pi * 2) * math.exp(-v * 12))) or (type == 'InOutRipple' and (1 - math.cos(2 * math.pi * v) * math.exp(-v * 4))) or (type == 'InOutShake' and (v * (1 - math.sin(v * math.pi * 4) * 0.1))) or (type == 'InfinityLoop' and (math.sin(v * math.pi * 2) * math.cos(v * math.pi * 2))) or (type == 'Heartbeat' and (1 + math.sin(v * math.pi * 2) * math.exp(-3 * v))) or -- batida de coracao oxe KKKKKKKKKKKK (type == 'SnailEase' and (math.pow(v, 1.5))) or -- oxe é um caracol é caralhp? (type == 'EchoWave' and (math.sin(v * math.pi * 2) / (1 + 5 * v))) or -- eco de onda (type == 'PingPong' and (math.abs(math.sin(v * math.pi)))) or (type == 'Boomerang' and (v <= 0.5 and (v * 2) or (1 - (v - 0.5) * 2))) or (type == 'Jelly' and (v * v * (3 - 2 * v) + 0.1 * math.sin(v * math.pi * 6))) or (type == 'TiltBounce' and (math.sin(v * math.pi * 2) * (1 - v))) or (type == 'LightningFlash' and (math.abs(math.sin(v * math.pi * 10)) * (1 - v))) or (type == 'BlinkingGlow' and (math.abs(math.sin(v * math.pi * 6)))) or (type == 'SlowSnapBack' and (v < 0.5 and (2 * v * v) or (1 - (2 * (1 - v) * (1 - v))))) or (type == 'RocketLaunch' and (v * v * math.exp(v))) or v end function isValidNumber(val) return type(val) == 'number' and val == val end function AnimLib:Interpolate3D(startX, startY, startZ, endX, endY, endZ, progress, easingType) if progress < 0 or progress > 1 then return endX, endY, endZ, error('Invalid argument') end if not (isValidNumber(startX) and isValidNumber(startY) and isValidNumber(startZ)) then return endX, endY, endZ, error('Invalid argument') end if not (isValidNumber(endX) and isValidNumber(endY) and isValidNumber(endZ)) then return 0, 0, 0, error('Invalid argument') end local progressI = self:Interpolate(easingType, progress) if progressI ~= progressI then return endX, endY, endZ end local x = startX + (endX - startX) * progressI local y = startY + (endY - startY) * progressI local z = startZ + (endZ - startZ) * progressI return x, y, z end function AnimLib:Create(name, values, ...) if type(name) == 'table' then values = name name = name.name or name.Name end if (not self.threads[name]) then self.threads[name] = { alreadyExecutedOnce = false, current = values.value or values.Value or values.startValue or values.target or values.Target or 0, target = values.value or values.Value or values.target or values.Target or 0, startValue = values.startValue or values.target or values.Target or 0, speed = values.speed or values.Speed, forceInt = values.forceInt or values.ForceInt or values.forceint or false, timeElapsed = 0, running = true, startTime = 0, endTime = 1, onComplete = values.onComplete or false, onProgress = values.onProgress or false, isFinished = false, variableHook = values.variableHook or false } return values.startValue or values.value or self:Get(name) else return (self:Get(name) or 0) end end function AnimLib:Exists(name) return self.threads[name] and true or false end function AnimLib:SetValues(name, values) local thread = self.threads[name] if thread then thread.current = values.current or thread.current thread.target = values.target or thread.target thread.startValue = values.startValue or thread.startValue thread.speed = values.speed or thread.speed thread.forceInt = values.forceInt or thread.forceInt thread.onComplete = values.onComplete or thread.onComplete thread.onProgress = values.onProgress or thread.onProgress thread.variableHook = values.variableHook or thread.variableHook return self:Get(name) end end function AnimLib:Edit(name, params) local thread = self.threads[name] if thread then if (thread.target ~= params.target or thread.target ~= params.target and thread.isFinished) then local now = getTickCount() thread.alreadyExecutedOnce = true thread.target = params.target thread.startValue = thread.current thread.startTime = now thread.endTime = thread.startTime + (params.speed or thread.speed or 800) thread.animType = params.animType or 'InOutQuad' thread.running = true thread.isFinished = false thread.timeElapsed = 0 if params.onComplete and type(params.onComplete) == 'function' then thread.onComplete = params.onComplete end if params.onProgress and type(params.onProgress) == 'function' then thread.onProgress = params.onProgress end if params.variableHook and type(params.variableHook) == 'function' then thread.variableHook = params.variableHook end return self:Get(name) else return self:Get(name) end else return self:Create(name, { current = params.target, target = params.value or 0, startValue = params.target or 1, speed = params.speed, forceInt = params.forceInt or params.ForceInt or false, onComplete = params.onComplete or false, onProgress = params.onProgress or false, variableHook = params.variableHook or false })--, error('Thread '..tostring(name)..' não existia.') end end function AnimLib:SetAttribute(name, attribute, value) local thread = self.threads[name] if thread then thread[attribute] = value return self:Get(name) end end function AnimLib:Update(sliceTime, name) if name then local thread = self.threads[name] self:UpdateThread(thread, sliceTime) else for name, thread in pairs(self.threads) do self:UpdateThread(thread, sliceTime, name) end end end function AnimLib:Get(name) return (self.threads[name] and self.threads[name].current or 0) end function AnimLib:Destroy(name) if name then if self.threads[name] then self.threads[name] = nil collectgarbage() end else self.threads = {} collectgarbage() end end function AnimLib:DestroyGroup(group) if (group) then for name, thread in pairs(self.threads) do if (string.find(name, group)) then self:Destroy(name) end end end end function AnimLib:GetData(name, event) local t = self.threads[name] return (event == 'isFinished' and t.isFinished) or (event == 'alreadyExecuted' and t.alreadyExecutedOnce) end -- function AnimLib:Lerp(a, b, t) return (a * (1 - t) + b * t) end function AnimLib:CreateLerp(index, start, finish, duration) if (not self.lerps[index]) then self.lerps[index] = start end self.lerps[index] = self:Lerp(self.lerps[index], finish, duration) return self.lerps[index] end function AnimLib:GetLerp(index) return self.lerps[index] end function AnimLib:DestroyLerp(index) if (self.lerps[index]) then self.lerps[index] = nil end end function AnimLib:CreateHover(index, r, g, b, a, condition, speed, animType, separatedValues, callback) if (not index) then return end local r, g, b, a, condition, speed, animType = r or 0, g or 0, b or 0, a or 0, (type(condition) == 'number' and condition or (condition and 1 or 0)), speed or 500, animType or 'InOutQuad' if (type(a) == 'number') then if (a > 1) then a = (a / 255) end elseif (type(a) == 'table') then for i, v in pairs(a) do if (type(v) == 'number') then if (v > 1) then a[i] = (v / 255) end end end end if (not self:Exists(index)) then self:Create(index, {target = condition, speed = speed, animType = animType, onProgress = callback}) end local animValue = self:Edit(index, {target = condition, speed = speed, animType = animType, onProgress = callback}) local function ReturnSpecifiedColor(color, animValue) if (type(color) == 'number') then return color end local colorToReturn = color[1] if (color[1] > color[2]) then colorToReturn = (color[1] - ((color[1] - color[2]) * animValue)) elseif (color[1] < color[2]) then colorToReturn = (color[1] + ((color[2] - color[1]) * animValue)) end if (color[3]) then return (colorToReturn + color[3]) end return colorToReturn end local rR, rG, rB, rA = ReturnSpecifiedColor(r, animValue), ReturnSpecifiedColor(g, animValue), ReturnSpecifiedColor(b, animValue), ReturnSpecifiedColor(a, animValue) if (separatedValues) then return rR, rG, rB, rA end if (rgba) then return rgba(rR, rG, rB, rA) end return tocolor(rR, rG, rB, (rA * 255)) end function AnimLib:CreateHovers(index, states, options) if (not index or type(states) ~= "table") then return end options = options or {} local defaultSpeed = options.speed or 500 local defaultAnimType = options.animType or "InOutQuad" local separatedValues = options.separatedValues local function normalizeA(v) if (type(v) == "number" and v > 1) then return (v / 255) end return v end local function MixValue(from, to, t) if (from > to) then return (from - ((from - to) * t)) elseif (from < to) then return (from + ((to - from) * t)) end return from end local function MixColor(fromR, fromG, fromB, fromA, toR, toG, toB, toA, t) local a1 = normalizeA(fromA or 1) local a2 = normalizeA(toA or 1) local r = MixValue(fromR, toR, t) local g = MixValue(fromG, toG, t) local b = MixValue(fromB, toB, t) local a = MixValue(a1, a2, t) return r, g, b, a end local base = nil for _, st in ipairs(states) do if st.default and st.color then base = st.color break end end if (not base) then base = { 0, 0, 0, 0 } end local r, g, b, a = base[1] or 0, base[2] or 0, base[3] or 0, normalizeA(base[4] or 0) for i = #states, 1, -1 do local st = states[i] if (st and not st.default and st.color) then local cond = (type(st.cond) == "number" and st.cond) or (st.cond and 1 or 0) local speed = st.speed or defaultSpeed local animType = st.animType or defaultAnimType local cb = st.callback or options.callback local animIndex = (index .. "_c" .. i) if (not self:Exists(animIndex)) then self:Create(animIndex, { target = cond, speed = speed, animType = animType, onProgress = cb }) end local t = self:Edit(animIndex, { target = cond, speed = speed, animType = animType, onProgress = cb }) local cr, cg, cb2, ca = st.color[1] or 0, st.color[2] or 0, st.color[3] or 0, normalizeA(st.color[4] or 0) r, g, b, a = MixColor(r, g, b, a, cr, cg, cb2, ca, t) end end if (separatedValues) then return r, g, b, a end if (rgba) then return rgba(r, g, b, a) end return tocolor(r, g, b, (a * 255)) end return AnimLib