flux.lua 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. --
  2. -- flux
  3. --
  4. -- Copyright (c) 2016 rxi
  5. --
  6. -- This library is free software; you can redistribute it and/or modify it
  7. -- under the terms of the MIT license. See LICENSE for details.
  8. --
  9. local flux = { _version = "0.1.5" }
  10. flux.__index = flux
  11. flux.tweens = {}
  12. flux.easing = { linear = function(p) return p end }
  13. local easing = {
  14. quad = "p * p",
  15. cubic = "p * p * p",
  16. quart = "p * p * p * p",
  17. quint = "p * p * p * p * p",
  18. expo = "2 ^ (10 * (p - 1))",
  19. sine = "-math.cos(p * (math.pi * .5)) + 1",
  20. circ = "-(math.sqrt(1 - (p * p)) - 1)",
  21. back = "p * p * (2.7 * p - 1.7)",
  22. elastic = "-(2^(10 * (p - 1)) * math.sin((p - 1.075) * (math.pi * 2) / .3))"
  23. }
  24. local makefunc = function(str, expr)
  25. local load = loadstring or load
  26. return load("return function(p) " .. str:gsub("%$e", expr) .. " end")()
  27. end
  28. for k, v in pairs(easing) do
  29. flux.easing[k .. "in"] = makefunc("return $e", v)
  30. flux.easing[k .. "out"] = makefunc([[
  31. p = 1 - p
  32. return 1 - ($e)
  33. ]], v)
  34. flux.easing[k .. "inout"] = makefunc([[
  35. p = p * 2
  36. if p < 1 then
  37. return .5 * ($e)
  38. else
  39. p = 2 - p
  40. return .5 * (1 - ($e)) + .5
  41. end
  42. ]], v)
  43. end
  44. local tween = {}
  45. tween.__index = tween
  46. local function makefsetter(field)
  47. return function(self, x)
  48. local mt = getmetatable(x)
  49. if type(x) ~= "function" and not (mt and mt.__call) then
  50. error("expected function or callable", 2)
  51. end
  52. local old = self[field]
  53. self[field] = old and function() old() x() end or x
  54. return self
  55. end
  56. end
  57. local function makesetter(field, checkfn, errmsg)
  58. return function(self, x)
  59. if checkfn and not checkfn(x) then
  60. error(errmsg:gsub("%$x", tostring(x)), 2)
  61. end
  62. self[field] = x
  63. return self
  64. end
  65. end
  66. tween.ease = makesetter("_ease",
  67. function(x) return flux.easing[x] end,
  68. "bad easing type '$x'")
  69. tween.delay = makesetter("_delay",
  70. function(x) return type(x) == "number" end,
  71. "bad delay time; expected number")
  72. tween.onstart = makefsetter("_onstart")
  73. tween.onupdate = makefsetter("_onupdate")
  74. tween.oncomplete = makefsetter("_oncomplete")
  75. function tween.new(obj, time, vars)
  76. local self = setmetatable({}, tween)
  77. self.obj = obj
  78. self.rate = time > 0 and 1 / time or 0
  79. self.progress = time > 0 and 0 or 1
  80. self._delay = 0
  81. self._ease = "quadout"
  82. self.vars = {}
  83. for k, v in pairs(vars) do
  84. if type(v) ~= "number" then
  85. error("bad value for key '" .. k .. "'; expected number")
  86. end
  87. self.vars[k] = v
  88. end
  89. return self
  90. end
  91. function tween:init()
  92. for k, v in pairs(self.vars) do
  93. local x = self.obj[k]
  94. if type(x) ~= "number" then
  95. error("bad value on object key '" .. k .. "'; expected number")
  96. end
  97. self.vars[k] = { start = x, diff = v - x }
  98. end
  99. self.inited = true
  100. end
  101. function tween:after(...)
  102. local t
  103. if select("#", ...) == 2 then
  104. t = tween.new(self.obj, ...)
  105. else
  106. t = tween.new(...)
  107. end
  108. t.parent = self.parent
  109. self:oncomplete(function() flux.add(self.parent, t) end)
  110. return t
  111. end
  112. function tween:stop()
  113. flux.remove(self.parent, self)
  114. end
  115. function flux.group()
  116. return setmetatable({}, flux)
  117. end
  118. function flux:to(obj, time, vars)
  119. return flux.add(self, tween.new(obj, time, vars))
  120. end
  121. function flux:update(deltatime)
  122. for i = #self, 1, -1 do
  123. local t = self[i]
  124. if t._delay > 0 then
  125. t._delay = t._delay - deltatime
  126. else
  127. if not t.inited then
  128. flux.clear(self, t.obj, t.vars)
  129. t:init()
  130. end
  131. if t._onstart then
  132. t._onstart()
  133. t._onstart = nil
  134. end
  135. t.progress = t.progress + t.rate * deltatime
  136. local p = t.progress
  137. local x = p >= 1 and 1 or flux.easing[t._ease](p)
  138. for k, v in pairs(t.vars) do
  139. t.obj[k] = v.start + x * v.diff
  140. end
  141. if t._onupdate then t._onupdate() end
  142. if p >= 1 then
  143. flux.remove(self, i)
  144. if t._oncomplete then t._oncomplete() end
  145. end
  146. end
  147. end
  148. end
  149. function flux:clear(obj, vars)
  150. for t in pairs(self[obj]) do
  151. if t.inited then
  152. for k in pairs(vars) do t.vars[k] = nil end
  153. end
  154. end
  155. end
  156. function flux:add(tween)
  157. -- Add to object table, create table if it does not exist
  158. local obj = tween.obj
  159. self[obj] = self[obj] or {}
  160. self[obj][tween] = true
  161. -- Add to array
  162. table.insert(self, tween)
  163. tween.parent = self
  164. return tween
  165. end
  166. function flux:remove(x)
  167. if type(x) == "number" then
  168. -- Remove from object table, destroy table if it is empty
  169. local obj = self[x].obj
  170. self[obj][self[x]] = nil
  171. if not next(self[obj]) then self[obj] = nil end
  172. -- Remove from array
  173. self[x] = self[#self]
  174. return table.remove(self)
  175. end
  176. for i, v in ipairs(self) do
  177. if v == x then
  178. return flux.remove(self, i)
  179. end
  180. end
  181. end
  182. local bound = {
  183. to = function(...) return flux.to(flux.tweens, ...) end,
  184. update = function(...) return flux.update(flux.tweens, ...) end,
  185. remove = function(...) return flux.remove(flux.tweens, ...) end,
  186. }
  187. setmetatable(bound, flux)
  188. return bound