added Flux package
This commit is contained in:
20
packages/flux/LICENSE
Normal file
20
packages/flux/LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2016 rxi
|
||||
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
159
packages/flux/README.md
Normal file
159
packages/flux/README.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# flux
|
||||
A fast, lightweight tweening library for Lua.
|
||||
|
||||
## Fork changes
|
||||
|
||||
Changes that I made to this library:
|
||||
|
||||
* Add the ability to chain with [tick](https://github.com/sheepolution/tick).
|
||||
```lua
|
||||
flux.to(t, 4, { x = 10 }):wait(2, function () print("Hello world") end)
|
||||
```
|
||||
* Add bounce easing (from [this pr](https://github.com/rxi/flux/pull/2)).
|
||||
* Add that object is passed to the oncomplete function (from [this pr](https://github.com/rxi/flux/pull/5)).
|
||||
* Add `tween:cycle` and `tween:rewind` for repeating and rewinding tweens (from [this pr](https://github.com/rxi/flux/pull/10)).
|
||||
* Change flux to be more precise (inspired by [this pr](https://github.com/rxi/flux/pull/3)).
|
||||
|
||||
## Installation
|
||||
The [flux.lua](flux.lua?raw=1) file should be dropped into an existing project
|
||||
and required by it.
|
||||
```lua
|
||||
flux = require "flux"
|
||||
```
|
||||
The `flux.update()` function should be called at the start of each frame. As
|
||||
its only argument It should be given the time in seconds that has passed since
|
||||
the last call.
|
||||
```lua
|
||||
flux.update(deltatime)
|
||||
```
|
||||
|
||||
## Usage
|
||||
Any number of numerical values in a table can be tweened simultaneously. Tweens
|
||||
are started by using the `flux.to()` function. This function requires 3
|
||||
arguments:
|
||||
* `obj` The object which contains the variables to tween
|
||||
* `time` The amount of time the tween should take to complete
|
||||
* `vars` A table where the keys correspond to the keys in `obj` which should be
|
||||
tweened, and their values correspond to the destination
|
||||
```lua
|
||||
-- Moves the ball object to the position 200, 300 over 4 seconds
|
||||
flux.to(ball, 4, { x = 200, y = 300 })
|
||||
```
|
||||
If you try to tween a variable which is already being tweened, the original
|
||||
tween stops tweening the variable and the new tween begins from the current
|
||||
value.
|
||||
|
||||
### Additional options
|
||||
Additional options when creating a tween can be set through the use of chained
|
||||
functions provided by the tween object which `flux.to()` returns.
|
||||
```lua
|
||||
flux.to(t, 4, { x = 10 }):ease("linear"):delay(1)
|
||||
```
|
||||
|
||||
#### :ease(type)
|
||||
The easing type which should be used by the tween; `type` should be a string
|
||||
containing the name of the easing to be used. The library provides the
|
||||
following easing types:
|
||||
|
||||
`linear`
|
||||
`quadin` `quadout` `quadinout`
|
||||
`cubicin` `cubicout` `cubicinout`
|
||||
`quartin` `quartout` `quartinout`
|
||||
`quintin` `quintout` `quintinout`
|
||||
`expoin` `expoout` `expoinout`
|
||||
`sinein` `sineout` `sineinout`
|
||||
`circin` `circout` `circinout`
|
||||
`backin` `backout` `backinout`
|
||||
`elasticin` `elasticout` `elasticinout`
|
||||
|
||||
The default easing type is `quadout`. Examples of the different easing types
|
||||
can be [found here](http://easings.net/).
|
||||
|
||||
|
||||
#### :delay(time)
|
||||
The amount of time flux should wait before starting the tween; `time` should be
|
||||
a number of seconds. The default delay time is `0`.
|
||||
|
||||
#### :onstart(fn)
|
||||
Sets the function `fn` to be called when the tween starts (once the delay has
|
||||
finished). `:onstart()` can be called multiple times to add more than one
|
||||
function.
|
||||
|
||||
#### :onupdate(fn)
|
||||
Sets the function `fn` to be called each frame the tween updates a value.
|
||||
`onupdate()` can be called multiple times to add more than one function.
|
||||
|
||||
#### :cycle(times|forever)
|
||||
The amount of times the tween should be repeated. If true is sent, it will
|
||||
repeat forever.
|
||||
|
||||
#### :rewind(times|forever)
|
||||
Similar to the previous one, with the difference that it doesn't start again from the
|
||||
initial state, it starts from the final state so it "bounces" back and forth.
|
||||
|
||||
#### :oncomplete(fn)
|
||||
Sets the function `fn` to be called once the tween has finished and reached its
|
||||
destination values. `oncomplete()` can be called multiple times to add more
|
||||
than one function.
|
||||
|
||||
#### :oncyclecomplete(fn)
|
||||
Sets the function `fn` to be called once the tween has finished one of its repetitions.
|
||||
|
||||
#### :onrewindcomplete(fn)
|
||||
Similar to the previous one, it calls the function `fn` when the tween finishes its
|
||||
transition from A to B or B to A.
|
||||
|
||||
#### :after([obj,] time, vars)
|
||||
Creates a new tween and chains it to the end of the existing tween; the chained
|
||||
tween will be called after the original one has finished. Any additional
|
||||
chained function used after `:after()` will effect the chained tween. There is
|
||||
no limit to how many times `:after()` can be used in a chain, allowing the
|
||||
creation of long tween sequences. If `obj` is not specified the `obj` argument
|
||||
from the original tween is used.
|
||||
```lua
|
||||
-- Tweens t.x to 10 over 2 seconds, then to 20 over 1 second
|
||||
flux.to(t, 2, { x = 10 }):after(t, 1, { x = 20 })
|
||||
```
|
||||
|
||||
### Stopping a tween
|
||||
If you want the ability to stop a tween before it has finished, the tween
|
||||
should be assigned to a variable when it is created.
|
||||
```lua
|
||||
local tween = flux.to(x, 2, { y = 20 }):delay(1)
|
||||
```
|
||||
The tween can then be stopped at any point by calling its `:stop()` method.
|
||||
```lua
|
||||
tween:stop()
|
||||
```
|
||||
This will cause the tween to immediatly be removed from its parent group and
|
||||
will leave its tweened variables at their current values. The tween's
|
||||
`oncomplete()` callback is not called.
|
||||
|
||||
### Groups
|
||||
flux provides the ability to create tween groups; these are objects
|
||||
which can have tweens added to them, and who are in charge of updating and
|
||||
handling their contained tweens. A group is created by calling the
|
||||
`flux.group()` function.
|
||||
```lua
|
||||
group = flux.group()
|
||||
```
|
||||
Once a group is created it acts independently of the `flux` object, and must
|
||||
be updated each frame using its own update method.
|
||||
```lua
|
||||
group:update(deltatime)
|
||||
```
|
||||
To add a tween to a group, the group's `to()` method should be used.
|
||||
```lua
|
||||
group:to(t, 3, { x = 10, y = 20 })
|
||||
```
|
||||
A good example of where groups are useful is for games where you may have a set
|
||||
of tweens which effect objects in the game world and which you want to pause
|
||||
when the game is paused. A group's tweens can be paused by simply neglecting
|
||||
to call its `update()` method; when a group is destroyed its tweens are also
|
||||
destroyed.
|
||||
|
||||
|
||||
## License
|
||||
This library is free software; you can redistribute it and/or modify it under
|
||||
the terms of the MIT license. See [LICENSE](LICENSE) for details.
|
||||
|
||||
294
packages/flux/init.lua
Normal file
294
packages/flux/init.lua
Normal file
@@ -0,0 +1,294 @@
|
||||
--
|
||||
-- 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))",
|
||||
bounce =
|
||||
"((1-p) < 1/2.75 and 1-(7.5625*(1-p)*(1-p))) or ((1-p) < (2/2.75) and 1-(7.5625*((1-p) - (1.5/2.75))*((1-p) - (1.5/2.75)) + .75)) or ((1-p) < 2.5/2.75 and 1-(7.5625*((1-p) - (2.25/2.75))*((1-p) - (2.25/2.75)) + .9375)) or 1-(7.5625*((1-p) - (2.625/2.75))*((1-p) - (2.625/2.75)) + .984375)"
|
||||
}
|
||||
|
||||
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.rewind = makesetter("_rewind",
|
||||
function(x) return type(x):match("[number|boolean]") end,
|
||||
"bad rewind time; expected number or boolean")
|
||||
tween.cycle = makesetter("_cycle",
|
||||
function(x) return type(x):match("[number|boolean]") end,
|
||||
"bad cycle value; expected number or boolean")
|
||||
tween.onstart = makefsetter("_onstart")
|
||||
tween.onupdate = makefsetter("_onupdate")
|
||||
tween.oncomplete = makefsetter("_oncomplete")
|
||||
tween.onrewindcomplete = makefsetter("_onrewindcomplete")
|
||||
tween.oncyclecomplete = makefsetter("_oncyclecomplete")
|
||||
|
||||
local function update(tween, i, deltatime)
|
||||
local t = tween[i]
|
||||
if not t or t.paused then
|
||||
return
|
||||
end
|
||||
t._dt = deltatime
|
||||
if t._delay > 0 then
|
||||
t._delay = t._delay - deltatime
|
||||
if t._delay <= 0 then
|
||||
t._dt = -t._delay
|
||||
t._delay = 0
|
||||
end
|
||||
end
|
||||
if t._delay > 0 then
|
||||
return
|
||||
end
|
||||
if t._dt <= 0 then
|
||||
return
|
||||
end
|
||||
if not t.inited then
|
||||
flux.clear(tween, t.obj, t.vars)
|
||||
t:init()
|
||||
end
|
||||
if t._onstart then
|
||||
t._onstart()
|
||||
t._onstart = nil
|
||||
end
|
||||
local remain = (1 - t.progress) / t.rate
|
||||
t.progress = t.progress + t.rate * t._dt
|
||||
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 * t.way
|
||||
end
|
||||
if t._onupdate then t._onupdate(deltatime) end
|
||||
if p < 1 then
|
||||
return
|
||||
end
|
||||
t._dt = t._dt - remain
|
||||
if t._rewind and (t._rewind == true or t._rewind > 1) then
|
||||
t.progress = 0
|
||||
t._rewind = (t._rewind == true) or (t._rewind - 1)
|
||||
t.way = t.way * -1
|
||||
for k, v in pairs(t.vars) do
|
||||
t.vars[k].start = t.obj[k]
|
||||
end
|
||||
if t._onrewindcomplete then t._onrewindcomplete() end
|
||||
elseif t._cycle and (t._cycle == true or t._cycle > 1) then
|
||||
t.progress = 0
|
||||
t._cycle = (t._cycle == true) or (t._cycle - 1)
|
||||
if t._oncyclecomplete then t._oncyclecomplete() end
|
||||
else
|
||||
flux.remove(tween, i)
|
||||
if t._oncomplete then t._oncomplete(t.obj) end
|
||||
if t.g_tick then t.g_tick.paused = false end
|
||||
end
|
||||
end
|
||||
|
||||
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 = {}
|
||||
self.way = 1
|
||||
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)
|
||||
update(self.parent, #self.parent, self._dt or 0)
|
||||
end)
|
||||
return t
|
||||
end
|
||||
|
||||
function tween:wait(t, f)
|
||||
local tick = self.parent._tick
|
||||
assert(tick, "No tick set!", 2)
|
||||
local td = tick:delay(t, f)
|
||||
td.paused = true
|
||||
td._flux = self.parent
|
||||
self.g_tick = td
|
||||
return td
|
||||
end
|
||||
|
||||
function tween:pause()
|
||||
self.paused = true
|
||||
end
|
||||
|
||||
function tween:resume()
|
||||
self.paused = false
|
||||
end
|
||||
|
||||
function tween:stop()
|
||||
flux.remove(self.parent, self)
|
||||
end
|
||||
|
||||
function flux.group()
|
||||
return setmetatable({}, flux)
|
||||
end
|
||||
|
||||
function flux:tick(lib)
|
||||
self._tick = lib
|
||||
end
|
||||
|
||||
function flux:to(obj, time, vars)
|
||||
return flux.add(self, tween.new(obj, time, vars))
|
||||
end
|
||||
|
||||
function flux:__call(...)
|
||||
return self:to(...)
|
||||
end
|
||||
|
||||
function flux:update(deltatime)
|
||||
for i = #self, 1, -1 do
|
||||
update(self, i, deltatime)
|
||||
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
|
||||
|
||||
if self._tick then
|
||||
tween._tick = self._tick
|
||||
end
|
||||
|
||||
-- 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 pairs(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
|
||||
Reference in New Issue
Block a user