| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- --
- -- flux
- --
- -- Copyright (c) 2016 rxi
- --
- -- This library is free software; you can redistribute it and/or modify it
- -- under the terms of the MIT license. See LICENSE for details.
- --
- local flux = { _version = "0.1.5" }
- flux.__index = flux
- flux.tweens = {}
- flux.easing = { linear = function(p) return p end }
- local easing = {
- quad = "p * p",
- cubic = "p * p * p",
- quart = "p * p * p * p",
- quint = "p * p * p * p * p",
- expo = "2 ^ (10 * (p - 1))",
- sine = "-math.cos(p * (math.pi * .5)) + 1",
- circ = "-(math.sqrt(1 - (p * p)) - 1)",
- back = "p * p * (2.7 * p - 1.7)",
- elastic = "-(2^(10 * (p - 1)) * math.sin((p - 1.075) * (math.pi * 2) / .3))"
- }
- local makefunc = function(str, expr)
- local load = loadstring or load
- return load("return function(p) " .. str:gsub("%$e", expr) .. " end")()
- end
- for k, v in pairs(easing) do
- flux.easing[k .. "in"] = makefunc("return $e", v)
- flux.easing[k .. "out"] = makefunc([[
- p = 1 - p
- return 1 - ($e)
- ]], v)
- flux.easing[k .. "inout"] = makefunc([[
- p = p * 2
- if p < 1 then
- return .5 * ($e)
- else
- p = 2 - p
- return .5 * (1 - ($e)) + .5
- end
- ]], v)
- end
- local tween = {}
- tween.__index = tween
- local function makefsetter(field)
- return function(self, x)
- local mt = getmetatable(x)
- if type(x) ~= "function" and not (mt and mt.__call) then
- error("expected function or callable", 2)
- end
- local old = self[field]
- self[field] = old and function() old() x() end or x
- return self
- end
- end
- local function makesetter(field, checkfn, errmsg)
- return function(self, x)
- if checkfn and not checkfn(x) then
- error(errmsg:gsub("%$x", tostring(x)), 2)
- end
- self[field] = x
- return self
- end
- end
- tween.ease = makesetter("_ease",
- function(x) return flux.easing[x] end,
- "bad easing type '$x'")
- tween.delay = makesetter("_delay",
- function(x) return type(x) == "number" end,
- "bad delay time; expected number")
- tween.onstart = makefsetter("_onstart")
- tween.onupdate = makefsetter("_onupdate")
- tween.oncomplete = makefsetter("_oncomplete")
- function tween.new(obj, time, vars)
- local self = setmetatable({}, tween)
- self.obj = obj
- self.rate = time > 0 and 1 / time or 0
- self.progress = time > 0 and 0 or 1
- self._delay = 0
- self._ease = "quadout"
- self.vars = {}
- for k, v in pairs(vars) do
- if type(v) ~= "number" then
- error("bad value for key '" .. k .. "'; expected number")
- end
- self.vars[k] = v
- end
- return self
- end
- function tween:init()
- for k, v in pairs(self.vars) do
- local x = self.obj[k]
- if type(x) ~= "number" then
- error("bad value on object key '" .. k .. "'; expected number")
- end
- self.vars[k] = { start = x, diff = v - x }
- end
- self.inited = true
- end
- function tween:after(...)
- local t
- if select("#", ...) == 2 then
- t = tween.new(self.obj, ...)
- else
- t = tween.new(...)
- end
- t.parent = self.parent
- self:oncomplete(function() flux.add(self.parent, t) end)
- return t
- end
- function tween:stop()
- flux.remove(self.parent, self)
- end
- function flux.group()
- return setmetatable({}, flux)
- end
- function flux:to(obj, time, vars)
- return flux.add(self, tween.new(obj, time, vars))
- end
- function flux:update(deltatime)
- for i = #self, 1, -1 do
- local t = self[i]
- if t._delay > 0 then
- t._delay = t._delay - deltatime
- else
- if not t.inited then
- flux.clear(self, t.obj, t.vars)
- t:init()
- end
- if t._onstart then
- t._onstart()
- t._onstart = nil
- end
- t.progress = t.progress + t.rate * deltatime
- local p = t.progress
- local x = p >= 1 and 1 or flux.easing[t._ease](p)
- for k, v in pairs(t.vars) do
- t.obj[k] = v.start + x * v.diff
- end
- if t._onupdate then t._onupdate() end
- if p >= 1 then
- flux.remove(self, i)
- if t._oncomplete then t._oncomplete() end
- end
- end
- end
- end
- function flux:clear(obj, vars)
- for t in pairs(self[obj]) do
- if t.inited then
- for k in pairs(vars) do t.vars[k] = nil end
- end
- end
- end
- function flux:add(tween)
- -- Add to object table, create table if it does not exist
- local obj = tween.obj
- self[obj] = self[obj] or {}
- self[obj][tween] = true
- -- Add to array
- table.insert(self, tween)
- tween.parent = self
- return tween
- end
- function flux:remove(x)
- if type(x) == "number" then
- -- Remove from object table, destroy table if it is empty
- local obj = self[x].obj
- self[obj][self[x]] = nil
- if not next(self[obj]) then self[obj] = nil end
- -- Remove from array
- self[x] = self[#self]
- return table.remove(self)
- end
- for i, v in ipairs(self) do
- if v == x then
- return flux.remove(self, i)
- end
- end
- end
- local bound = {
- to = function(...) return flux.to(flux.tweens, ...) end,
- update = function(...) return flux.update(flux.tweens, ...) end,
- remove = function(...) return flux.remove(flux.tweens, ...) end,
- }
- setmetatable(bound, flux)
- return bound
|