tween.lua
本代码基于tween.js移植local socket = require "socket"
local _nextId = 0
local nextId = function()
_nextId = _nextId + 1
return _nextId
end
local TweenGroups = {}
local Groups_mt = {__index = TweenGroups}
function TweenGroups:add(group)
self._groups[group:getId()] = group
end
function TweenGroups:remove(group)
self._groups[group:getId()] = nil
end
function TweenGroups:update(time, preserve)
for k ,v in pairs(self._groups) do
v:update(time, preserve)
end
end
function TweenGroups.new()
return setmetatable(
{
_groups = {},
}, Groups_mt)
end
tweenGroups = TweenGroups.new()
tween =
{
}
local pow, sin, cos, pi, sqrt, abs, asin = math.pow, math.sin, math.cos, math.pi, math.sqrt, math.abs, math.asin
-- linear
local function linear(k)
return k
end
-- quad
local function inQuad(k) return k * k end
local function outQuad(k)
return k * (2 - k)
end
local function inOutQuad(k)
k = k * 2
if k < 1 then
return 0.5 * k * k
end
k = k - 1
return - 0.5 * (k * (k - 2) - 1)
end
-- cubic
local function inCubic (k) return k * k * k end
local function outCubic(k)
k = k - 1
return k * k * k + 1
end
local function inOutCubic(k)
k = k * 2
if k < 1 then
return 0.5 * k * k * k
end
k = k - 2
return 0.5 * (k * k * k + 2)
end
-- quart
local function inQuart(k) return k * k * k * k end
local function outQuart(k)
k = k - 1
return 1 - (k * k * k * k)
end
local function inOutQuart(k)
k = k * 2
if k < 1 then
return 0.5 * k * k * k * k
end
k = k - 2
return - 0.5 * (k * k * k * k - 2)
end
-- quint
local function inQuint(k) return k * k * k * k * k end
local function outQuint(k)
k = k - 1
return k * k * k * k * k + 1
end
local function inOutQuint(k)
k = k * 2
if k < 1 then
return 0.5 * k * k * k * k * k
end
k = k - 2
return 0.5 * (k * k * k * k * k + 2);
end
-- sine
local function inSine(k) return 1 - cos(k * pi / 2) end
local function outSine(k) return sin(k * pi / 2) end
local function inOutSine(k) return 0.5 * (1 - cos(pi * k)) end
-- expo
local function inExpo(k)
if k == 0 then
return 0
end
return pow(1024, k - 1)
end
local function outExpo(k)
if k == 1 then
return 1
end
return 1 - pow(2, - 10 * k)
end
local function inOutExpo(k)
if k == 0 then
return 0
end
if k == 1 then
return 1
end
k = k * 2
if k < 1 then
return 0.5 * pow(1024, k - 1)
end
return 0.5 * (- pow(2, - 10 * (k - 1)) + 2)
end
-- circ
local function inCirc(k) return 1 - sqrt(1 - k * k) end
local function outCirc(k)
k = k - 1
return sqrt(1 - (k * k))
end
local function inOutCirc(k)
k = k * 2
if k < 1 then
return - 0.5 * (sqrt(1 - k * k) - 1)
end
k = k - 2
return 0.5 * (sqrt(1 - k * k) + 1)
end
-- elastic
local function inElastic(k)
if k == 0 then
return 0
end
if k == 1 then
return 1
end
return -pow(2, 10 * (k - 1)) * sin((k - 1.1) * 5 * pi)
end
local function outElastic(k)
if k == 0 then
return 0
end
if k == 1 then
return 1
end
return pow(2, -10 * k) * sin((k - 0.1) * 5 * pi) + 1
end
local function inOutElastic(k)
if k == 0 then
return 0
end
if k == 1 then
return 1
end
k = k * 2
if k < 1 then
return -0.5 * pow(2, 10 * (k - 1)) * sin((k - 1.1) * 5 * pi)
end
return 0.5 * pow(2, -10 * (k - 1)) * sin((k - 1.1) * 5 * pi) + 1
end
-- back
local function inBack(k)
local s = 1.70158
return k * k * ((s + 1) * k - s)
end
local function outBack(k)
local s = 1.70158
k = k - 1
return k * k * ((s + 1) * k + s) + 1
end
local function inOutBack(k)
local s = 1.70158 * 1.525
k = k * 2
if k < 1 then
return 0.5 * (k * k * ((s + 1) * k - s))
end
k = k - 2
return 0.5 * (k * k * ((s + 1) * k + s) + 2)
end
-- bounce
local function outBounce(k)
if k < (1 / 2.75) then
return 7.5625 * k * k
elseif (k < (2 / 2.75)) then
k = k - (1.5 / 2.75)
return 7.5625 * k * k + 0.75
elseif (k < (2.5 / 2.75)) then
k = k - (2.25 / 2.75)
return 7.5625 * k * k + 0.9375
else
k = k - (2.625 / 2.75)
return 7.5625 * k * k + 0.984375
end
end
local function inBounce(k)
return 1 - outBounce(1 - k)
end
local function inOutBounce(k)
if k < 0.5 then
return inBounce(k * 2) * 0.5
end
return outBounce(k * 2 - 1) * 0.5 + 0.5
end
local function random(k)
return math.random()
end
tween.easing =
{
linear = linear,
inQuad = inQuad, outQuad = outQuad, inOutQuad = inOutQuad,
inCubic = inCubic, outCubic = outCubic, inOutCubic = inOutCubic,
inQuart = inQuart, outQuart = outQuart, inOutQuart = inOutQuart,
inQuint = inQuint, outQuint = outQuint, inOutQuint = inOutQuint,
inSine = inSine, outSine = outSine, inOutSine = inOutSine,
inExpo = inExpo, outExpo = outExpo, inOutExpo = inOutExpo,
inCirc = inCirc, outCirc = outCirc, inOutCirc = inOutCirc,
inElastic = inElastic, outElastic = outElastic, inOutElastic = inOutElastic,
inBack = inBack, outBack = outBack, inOutBack = inOutBack,
inBounce = inBounce, outBounce = outBounce, inOutBounce = inOutBounce,
random = random
}
local function getEasingFunction(easing)
easing = easing or "linear"
if type(easing) == 'string' then
local name = easing
easing = tween.easing[name]
if type(easing) ~= 'function' then
error("The easing function name '" .. name .. "' is invalid")
end
end
return easing
end
local Tween = {}
local Tween_mt = {__index = Tween}
function Tween:getId()
return self._id
end
function Tween:isPlaying()
return self._isPlaying
end
function Tween:to(properties, duration)
self._group:add(self)
self._valuesEnd = properties
for property, v in pairs(self._valuesEnd) do
self._valuesEndBak[property] = self._valuesEnd[property]
end
if duration ~= nil then
self._duration = duration
end
return self
end
function Tween:start(time)
if self._isPause == true then
self._pauseEndTime = socket.gettime() * 1000
self._isPause = false
if self._pauseStartTime < self._startTime then
self._startTime = self._pauseEndTime
self._pauseStartTime = 0
self._pauseEndTime = 0
end
return
end
if self._isPlaying then
return
end
self._isPlaying = true
self._onStartCallbackFired = false
if time ~= nil then
if type(time) == "string" then
self._startTime = socket.gettime() * 1000 + tonumber(time)
else
self._startTime = time
end
else
self._startTime = socket.gettime() * 1000
end
self._startTime = self._startTime + self._delayTime
for property, v in pairs(self._valuesEnd) do
-- If `to()` specifies a property that doesn't exist in the source object,
-- we should not set that property in the object
if self._object[property] ~= nil then
-- Save the starting value.
self._valuesStart[property] = self._object[property]
self._valuesStartBak[property] = self._object[property]
end
self._valuesStartRepeat[property] = self._valuesStart[property] or 0
end
return self
end
function Tween:pause()
if self._isPause == true then
return
end
self._pauseStartTime = socket.gettime() * 1000
self._isPause = true
end
function Tween:stop()
self._isPlaying = false
self._isPause = false
for property, v in pairs(self._valuesStartBak) do
self._object[property] = self._valuesStartBak[property]
self._valuesStart[property] = self._valuesStartBak[property]
end
for property, v in pairs(self._valuesEndBak) do
self._valuesEnd[property] = self._valuesEndBak[property]
end
self._repeat = self._repeatBak
self._startTime = nil
self._yoyoCount = 0
if self._onStopCallback ~= nil then
self._onStopCallback(self._instance, self._propertyName, self._valuesStart)
end
return self
end
function Tween:endEXT()
self:update(self._startTime + self._duration)
return self
end
function Tween:delay(amount)
self._delayTime = amount
return self
end
function Tween:repeatEXT(times)
self._repeatBak = times
self._repeat = times
return self
end
function Tween:repeatDelay(amount)
self._repeatDelayTime = amount
return self
end
function Tween:yoyo(yy)
self._yoyo = yy
return self
end
function Tween:easing(eas)
self._easingFunction = getEasingFunction(eas)
return self
end
function Tween:interpolation(inter)
self._interpolationFunction = inter
return self
end
function Tween:chain()
self._chainedTweens = arguments
return self
end
function Tween:onStart(callback)
self._onStartCallback = callback
return self
end
function Tween:onUpdate(callback)
self._onUpdateCallback = callback
return self
end
function Tween:onComplete(callback)
self._onCompleteCallback = callback
return self
end
function Tween:onStop(callback)
self._onStopCallback = callback
return self
end
function Tween:onLoop(callback)
self._onLoopCallback = callback
return self
end
function Tween:update(time)
local property = 0
local elapsed = 0
local value = 0
if self._isPlaying == false then
return true
end
if time < self._startTime then
return true
end
if self._onStartCallbackFired == false then
if self._onStartCallback ~= nil then
self._onStartCallback(self._object)
end
self._onStartCallbackFired = true
end
if self._isPause == true then
return true
end
local pauseTime = 0
if self._pauseStartTime ~= 0 and self._pauseEndTime ~= 0 then
pauseTime = self._pauseEndTime - self._pauseStartTime
end
elapsed = (time - self._startTime - pauseTime) / self._duration
elapsed = (self._duration == 0 or elapsed > 1) and 1 or elapsed
value = self._easingFunction(elapsed)
for property, v in pairs(self._valuesEnd) do
-- Don't update properties that do not exist in the source object
if self._valuesStart[property] ~= nil then
local startValue = self._valuesStart[property] or 0
local endValue = self._valuesEnd[property]
-- Protect against non numeric properties.
if type(endValue) == 'number' then
self._object[property] = startValue + (endValue - startValue) * value;
local max = self._valuesStartBak[property]
local min = self._valuesEndBak[property]
if max < min then
local t = max
max = min
min = t
end
if self._object[property] > max then
self._object[property] = max
end
if self._object[property] < min then
self._object[property] = min
end
end
end
end
if self._onUpdateCallback ~= nil then
self._onUpdateCallback(self._instance, self._propertyName, self._object)
end
if elapsed == 1 then
self._pauseStartTime = 0
self._pauseEndTime = 0
self._yoyoCount = self._yoyoCount + 1
if self._repeat == "Infinity" or self._repeat > 0 then
if self._repeat ~= "Infinity" then
self._repeat = self._repeat - 1
end
-- Reassign starting values, restart by making startTime = now
for property, v in pairs(self._valuesStartRepeat) do
if type(self._valuesEnd[property]) == 'string' then
self._valuesStartRepeat[property] = self._valuesStartRepeat[property] + tonumber(self._valuesEnd[property])
end
if self._yoyo == true then
local tmp = self._valuesStartRepeat[property]
self._valuesStartRepeat[property] = self._valuesEnd[property]
self._valuesEnd[property] = tmp
end
self._valuesStart[property] = self._valuesStartRepeat[property]
end
local function LoopCallbackFun(self)
if self._onLoopCallback ~= nil then
local tweenIds = table.keys(self._group._tweens)
self._group._LoopCallbackCount = self._group._LoopCallbackCount + 1
if self._group._LoopCallbackCount == #tweenIds then
self._onLoopCallback(self._object)
self._group._LoopCallbackCount = 0
self._isComplete = true
end
end
end
if self._yoyo == true then
if self._yoyoCount == 2 then
self._yoyoCount = 0
LoopCallbackFun(self)
end
else
LoopCallbackFun(self)
end
self._startTime = time
if self._repeatDelayTime ~= nil then
self._startTime = time + self._repeatDelayTime
end
return true
else
if self._onCompleteCallback ~= nil then
local tweenIds = table.keys(self._group._tweens)
self._group._CompleteCallbackCount = self._group._CompleteCallbackCount + 1
if self._group._CompleteCallbackCount == #tweenIds then
self._group._CompleteCallbackCount = 0
self._group._isComplete = true
self._onCompleteCallback(self._object)
if self._group._isCompletePlay == true then
self._group._isCompletePlay = false
return nil
end
return false
end
end
return false
end
end
return true
end
function tween.new(instance, propertyName, object, group)
return setmetatable(
{
_instance = instance,
_propertyName = propertyName,
_object = object,
_valuesStart = {},
_valuesEnd = {},
_valuesStartRepeat = {},
_duration = 1000,
_repeat = 0,
_repeatDelayTime = nil,
_yoyo = false,
_isPlaying = false,
_reversed = false,
_delayTime = 0,
_startTime = nil,
_easingFunction = getEasingFunction("linear"),
_onStartCallback = nil,
_onStartCallbackFired = false,
_onUpdateCallback = nil,
_onCompleteCallback = nil,
_onStopCallback = nil,
_onLoopCallback = nil,
_group = group or tweenGroup.new(),
_id = nextId(),
_isPause = false,
_pauseStartTime = 0,
_pauseEndTime = 0,
_valuesStartBak = {},
_valuesEndBak = {},
_repeatBak = 0,
_yoyoCount = 0
}, Tween_mt)
end
tweenGroup =
{
}
local TweenGroup = {}
local Group_mt = {__index = TweenGroup}
function TweenGroup:getId()
return self._id
end
function TweenGroup:getAll()
return _tweens
end
function TweenGroup:removeAll()
self._tweens = {}
end
function TweenGroup:add(tween)
self._tweens[tween:getId()] = tween
self._tweensAddedDuringUpdate[tween:getId()] = tween
end
function TweenGroup:remove(tween)
self._tweens[tween:getId()] = nil
self._tweensAddedDuringUpdate[tween:getId()] = nil
end
function TweenGroup:play()
if self._isComplete == true then
self:Restart()
self._isComplete = false
self._isCompletePlay = true
return
end
for k, v in pairs(self._tweens) do
v:start()
end
end
function TweenGroup:stop()
for k, v in pairs(self._tweens) do
v:stop()
end
self._LoopCallbackCount = 0
self._CompleteCallbackCount = 0
self._isComplete = false
self._isCompletePlay = false
end
function TweenGroup:pause()
for k, v in pairs(self._tweens) do
v:pause()
end
end
function TweenGroup:update(time, preserve)
if self._isPause then
return
end
local tweenIds = table.keys(self._tweens)
if #tweenIds == 0 then
return false
end
if time == nil then
time = socket.gettime() * 1000
end
-- Tweens are updated in "batches". If you add a new tween during an update, then the
-- new tween will be updated in the next batch.
-- If you remove a tween during an update, it may or may not be updated. However,
-- if the removed tween was added during the current batch, then it will not be updated.
while #tweenIds > 0 do
self._tweensAddedDuringUpdate = {}
for i = 1, #tweenIds do
local tween = self._tweens[tweenIds[i]]
if (tween ~= nil and tween:update(time) == false) then
tween._isPlaying = false
if (preserve == false) then
self._tweens[tweenIds[i]] = nil
end
end
end
tweenIds = table.keys(self._tweensAddedDuringUpdate)
end
return
end
local function __Play(self)
self:play()
end
local function __Stop(self)
self:stop()
end
local function __Pause(self)
self:pause()
end
local function __Restart(self)
self:Stop()
self:Play()
end
local function __OnComplete(self, callback)
for k, v in pairs(self._tweens) do
v:onComplete(callback)
end
end
local function __OnLoop(self, callback)
for k, v in pairs(self._tweens) do
v:onLoop(callback)
end
end
local function __createSelfProperty()
return
{
_tweens = {},
_tweensAddedDuringUpdate = {},
_id = nextId(),
_CompleteCallbackCount = 0,
_LoopCallbackCount = 0,
_isComplete = false,
_isCompletePlay = false,
Play = __Play,
Stop = __Stop,
Pause = __Pause,
Restart = __Restart,
OnComplete = __OnComplete,
OnLoop = __OnLoop
}
end
function tweenGroup.new()
local group = {}
table.merge(group, __createSelfProperty())
return setmetatable(group, Group_mt)
end
function createTween(self, instance, tweenInfo, propertyTable)
local group = tweenGroup.new()
for k, v in pairs(propertyTable) do
if instance[k] ~= nil then
local a = {}
if type(instance[k]) == "number" then
a[k] = instance[k]
else
a = instance[k]
end
local tween = tween.new(instance, k, a, group)
:onUpdate(function(instance, propertyName, propertyValue)
if table.nums(propertyValue) == 1 then
instance[propertyName] = propertyValue[propertyName]
else
instance[propertyName] = propertyValue
end
end)
:onStop(function(instance, propertyName, propertyValue)
if table.nums(propertyValue) == 1 then
instance[propertyName] = propertyValue[propertyName]
else
instance[propertyName] = propertyValue
end
end)
if tweenInfo["duration"] ~= nil then
local b = {}
if type(v) == "number" then
b[k] = v
else
b = v
end
tween:to(b, tweenInfo["duration"])
end
if tweenInfo["repeatCount"] ~= nil then
if tweenInfo["repeatCount"] <= 0 then
tween:repeatEXT("Infinity")
else
tween:repeatEXT(tweenInfo["repeatCount"]-1)
end
end
if tweenInfo["yoyo"] ~= nil then
tween:yoyo(tweenInfo["yoyo"])
if tweenInfo["yoyo"] == true and tweenInfo["repeatCount"] ~= nil then
if tweenInfo["repeatCount"] > 0 then
local n = tweenInfo["repeatCount"] * 2 - 1
tween:repeatEXT(n)
end
end
end
if tweenInfo["easing"] ~= nil then
tween:easing(tweenInfo["easing"])
end
if tweenInfo["delay"] ~= nil then
tween:delay(tweenInfo["delay"])
end
if tweenInfo["repeatDelay"] ~= nil then
tween:repeatDelay(tweenInfo["repeatDelay"])
end
end
end
tweenGroups:add(group)
return group
end
local _nextId = 0
local nextId = function()
_nextId = _nextId + 1
return _nextId
end
local TweenGroups = {}
local Groups_mt = {__index = TweenGroups}
function TweenGroups:add(group)
self._groups[group:getId()] = group
end
function TweenGroups:remove(group)
self._groups[group:getId()] = nil
end
function TweenGroups:update(time, preserve)
for k ,v in pairs(self._groups) do
v:update(time, preserve)
end
end
function TweenGroups.new()
return setmetatable(
{
_groups = {},
}, Groups_mt)
end
tweenGroups = TweenGroups.new()
tween =
{
}
local pow, sin, cos, pi, sqrt, abs, asin = math.pow, math.sin, math.cos, math.pi, math.sqrt, math.abs, math.asin
-- linear
local function linear(k)
return k
end
-- quad
local function inQuad(k) return k * k end
local function outQuad(k)
return k * (2 - k)
end
local function inOutQuad(k)
k = k * 2
if k < 1 then
return 0.5 * k * k
end
k = k - 1
return - 0.5 * (k * (k - 2) - 1)
end
-- cubic
local function inCubic (k) return k * k * k end
local function outCubic(k)
k = k - 1
return k * k * k + 1
end
local function inOutCubic(k)
k = k * 2
if k < 1 then
return 0.5 * k * k * k
end
k = k - 2
return 0.5 * (k * k * k + 2)
end
-- quart
local function inQuart(k) return k * k * k * k end
local function outQuart(k)
k = k - 1
return 1 - (k * k * k * k)
end
local function inOutQuart(k)
k = k * 2
if k < 1 then
return 0.5 * k * k * k * k
end
k = k - 2
return - 0.5 * (k * k * k * k - 2)
end
-- quint
local function inQuint(k) return k * k * k * k * k end
local function outQuint(k)
k = k - 1
return k * k * k * k * k + 1
end
local function inOutQuint(k)
k = k * 2
if k < 1 then
return 0.5 * k * k * k * k * k
end
k = k - 2
return 0.5 * (k * k * k * k * k + 2);
end
-- sine
local function inSine(k) return 1 - cos(k * pi / 2) end
local function outSine(k) return sin(k * pi / 2) end
local function inOutSine(k) return 0.5 * (1 - cos(pi * k)) end
-- expo
local function inExpo(k)
if k == 0 then
return 0
end
return pow(1024, k - 1)
end
local function outExpo(k)
if k == 1 then
return 1
end
return 1 - pow(2, - 10 * k)
end
local function inOutExpo(k)
if k == 0 then
return 0
end
if k == 1 then
return 1
end
k = k * 2
if k < 1 then
return 0.5 * pow(1024, k - 1)
end
return 0.5 * (- pow(2, - 10 * (k - 1)) + 2)
end
-- circ
local function inCirc(k) return 1 - sqrt(1 - k * k) end
local function outCirc(k)
k = k - 1
return sqrt(1 - (k * k))
end
local function inOutCirc(k)
k = k * 2
if k < 1 then
return - 0.5 * (sqrt(1 - k * k) - 1)
end
k = k - 2
return 0.5 * (sqrt(1 - k * k) + 1)
end
-- elastic
local function inElastic(k)
if k == 0 then
return 0
end
if k == 1 then
return 1
end
return -pow(2, 10 * (k - 1)) * sin((k - 1.1) * 5 * pi)
end
local function outElastic(k)
if k == 0 then
return 0
end
if k == 1 then
return 1
end
return pow(2, -10 * k) * sin((k - 0.1) * 5 * pi) + 1
end
local function inOutElastic(k)
if k == 0 then
return 0
end
if k == 1 then
return 1
end
k = k * 2
if k < 1 then
return -0.5 * pow(2, 10 * (k - 1)) * sin((k - 1.1) * 5 * pi)
end
return 0.5 * pow(2, -10 * (k - 1)) * sin((k - 1.1) * 5 * pi) + 1
end
-- back
local function inBack(k)
local s = 1.70158
return k * k * ((s + 1) * k - s)
end
local function outBack(k)
local s = 1.70158
k = k - 1
return k * k * ((s + 1) * k + s) + 1
end
local function inOutBack(k)
local s = 1.70158 * 1.525
k = k * 2
if k < 1 then
return 0.5 * (k * k * ((s + 1) * k - s))
end
k = k - 2
return 0.5 * (k * k * ((s + 1) * k + s) + 2)
end
-- bounce
local function outBounce(k)
if k < (1 / 2.75) then
return 7.5625 * k * k
elseif (k < (2 / 2.75)) then
k = k - (1.5 / 2.75)
return 7.5625 * k * k + 0.75
elseif (k < (2.5 / 2.75)) then
k = k - (2.25 / 2.75)
return 7.5625 * k * k + 0.9375
else
k = k - (2.625 / 2.75)
return 7.5625 * k * k + 0.984375
end
end
local function inBounce(k)
return 1 - outBounce(1 - k)
end
local function inOutBounce(k)
if k < 0.5 then
return inBounce(k * 2) * 0.5
end
return outBounce(k * 2 - 1) * 0.5 + 0.5
end
local function random(k)
return math.random()
end
tween.easing =
{
linear = linear,
inQuad = inQuad, outQuad = outQuad, inOutQuad = inOutQuad,
inCubic = inCubic, outCubic = outCubic, inOutCubic = inOutCubic,
inQuart = inQuart, outQuart = outQuart, inOutQuart = inOutQuart,
inQuint = inQuint, outQuint = outQuint, inOutQuint = inOutQuint,
inSine = inSine, outSine = outSine, inOutSine = inOutSine,
inExpo = inExpo, outExpo = outExpo, inOutExpo = inOutExpo,
inCirc = inCirc, outCirc = outCirc, inOutCirc = inOutCirc,
inElastic = inElastic, outElastic = outElastic, inOutElastic = inOutElastic,
inBack = inBack, outBack = outBack, inOutBack = inOutBack,
inBounce = inBounce, outBounce = outBounce, inOutBounce = inOutBounce,
random = random
}
local function getEasingFunction(easing)
easing = easing or "linear"
if type(easing) == 'string' then
local name = easing
easing = tween.easing[name]
if type(easing) ~= 'function' then
error("The easing function name '" .. name .. "' is invalid")
end
end
return easing
end
local Tween = {}
local Tween_mt = {__index = Tween}
function Tween:getId()
return self._id
end
function Tween:isPlaying()
return self._isPlaying
end
function Tween:to(properties, duration)
self._group:add(self)
self._valuesEnd = properties
for property, v in pairs(self._valuesEnd) do
self._valuesEndBak[property] = self._valuesEnd[property]
end
if duration ~= nil then
self._duration = duration
end
return self
end
function Tween:start(time)
if self._isPause == true then
self._pauseEndTime = socket.gettime() * 1000
self._isPause = false
if self._pauseStartTime < self._startTime then
self._startTime = self._pauseEndTime
self._pauseStartTime = 0
self._pauseEndTime = 0
end
return
end
if self._isPlaying then
return
end
self._isPlaying = true
self._onStartCallbackFired = false
if time ~= nil then
if type(time) == "string" then
self._startTime = socket.gettime() * 1000 + tonumber(time)
else
self._startTime = time
end
else
self._startTime = socket.gettime() * 1000
end
self._startTime = self._startTime + self._delayTime
for property, v in pairs(self._valuesEnd) do
-- If `to()` specifies a property that doesn't exist in the source object,
-- we should not set that property in the object
if self._object[property] ~= nil then
-- Save the starting value.
self._valuesStart[property] = self._object[property]
self._valuesStartBak[property] = self._object[property]
end
self._valuesStartRepeat[property] = self._valuesStart[property] or 0
end
return self
end
function Tween:pause()
if self._isPause == true then
return
end
self._pauseStartTime = socket.gettime() * 1000
self._isPause = true
end
function Tween:stop()
self._isPlaying = false
self._isPause = false
for property, v in pairs(self._valuesStartBak) do
self._object[property] = self._valuesStartBak[property]
self._valuesStart[property] = self._valuesStartBak[property]
end
for property, v in pairs(self._valuesEndBak) do
self._valuesEnd[property] = self._valuesEndBak[property]
end
self._repeat = self._repeatBak
self._startTime = nil
self._yoyoCount = 0
if self._onStopCallback ~= nil then
self._onStopCallback(self._instance, self._propertyName, self._valuesStart)
end
return self
end
function Tween:endEXT()
self:update(self._startTime + self._duration)
return self
end
function Tween:delay(amount)
self._delayTime = amount
return self
end
function Tween:repeatEXT(times)
self._repeatBak = times
self._repeat = times
return self
end
function Tween:repeatDelay(amount)
self._repeatDelayTime = amount
return self
end
function Tween:yoyo(yy)
self._yoyo = yy
return self
end
function Tween:easing(eas)
self._easingFunction = getEasingFunction(eas)
return self
end
function Tween:interpolation(inter)
self._interpolationFunction = inter
return self
end
function Tween:chain()
self._chainedTweens = arguments
return self
end
function Tween:onStart(callback)
self._onStartCallback = callback
return self
end
function Tween:onUpdate(callback)
self._onUpdateCallback = callback
return self
end
function Tween:onComplete(callback)
self._onCompleteCallback = callback
return self
end
function Tween:onStop(callback)
self._onStopCallback = callback
return self
end
function Tween:onLoop(callback)
self._onLoopCallback = callback
return self
end
function Tween:update(time)
local property = 0
local elapsed = 0
local value = 0
if self._isPlaying == false then
return true
end
if time < self._startTime then
return true
end
if self._onStartCallbackFired == false then
if self._onStartCallback ~= nil then
self._onStartCallback(self._object)
end
self._onStartCallbackFired = true
end
if self._isPause == true then
return true
end
local pauseTime = 0
if self._pauseStartTime ~= 0 and self._pauseEndTime ~= 0 then
pauseTime = self._pauseEndTime - self._pauseStartTime
end
elapsed = (time - self._startTime - pauseTime) / self._duration
elapsed = (self._duration == 0 or elapsed > 1) and 1 or elapsed
value = self._easingFunction(elapsed)
for property, v in pairs(self._valuesEnd) do
-- Don't update properties that do not exist in the source object
if self._valuesStart[property] ~= nil then
local startValue = self._valuesStart[property] or 0
local endValue = self._valuesEnd[property]
-- Protect against non numeric properties.
if type(endValue) == 'number' then
self._object[property] = startValue + (endValue - startValue) * value;
local max = self._valuesStartBak[property]
local min = self._valuesEndBak[property]
if max < min then
local t = max
max = min
min = t
end
if self._object[property] > max then
self._object[property] = max
end
if self._object[property] < min then
self._object[property] = min
end
end
end
end
if self._onUpdateCallback ~= nil then
self._onUpdateCallback(self._instance, self._propertyName, self._object)
end
if elapsed == 1 then
self._pauseStartTime = 0
self._pauseEndTime = 0
self._yoyoCount = self._yoyoCount + 1
if self._repeat == "Infinity" or self._repeat > 0 then
if self._repeat ~= "Infinity" then
self._repeat = self._repeat - 1
end
-- Reassign starting values, restart by making startTime = now
for property, v in pairs(self._valuesStartRepeat) do
if type(self._valuesEnd[property]) == 'string' then
self._valuesStartRepeat[property] = self._valuesStartRepeat[property] + tonumber(self._valuesEnd[property])
end
if self._yoyo == true then
local tmp = self._valuesStartRepeat[property]
self._valuesStartRepeat[property] = self._valuesEnd[property]
self._valuesEnd[property] = tmp
end
self._valuesStart[property] = self._valuesStartRepeat[property]
end
local function LoopCallbackFun(self)
if self._onLoopCallback ~= nil then
local tweenIds = table.keys(self._group._tweens)
self._group._LoopCallbackCount = self._group._LoopCallbackCount + 1
if self._group._LoopCallbackCount == #tweenIds then
self._onLoopCallback(self._object)
self._group._LoopCallbackCount = 0
self._isComplete = true
end
end
end
if self._yoyo == true then
if self._yoyoCount == 2 then
self._yoyoCount = 0
LoopCallbackFun(self)
end
else
LoopCallbackFun(self)
end
self._startTime = time
if self._repeatDelayTime ~= nil then
self._startTime = time + self._repeatDelayTime
end
return true
else
if self._onCompleteCallback ~= nil then
local tweenIds = table.keys(self._group._tweens)
self._group._CompleteCallbackCount = self._group._CompleteCallbackCount + 1
if self._group._CompleteCallbackCount == #tweenIds then
self._group._CompleteCallbackCount = 0
self._group._isComplete = true
self._onCompleteCallback(self._object)
if self._group._isCompletePlay == true then
self._group._isCompletePlay = false
return nil
end
return false
end
end
return false
end
end
return true
end
function tween.new(instance, propertyName, object, group)
return setmetatable(
{
_instance = instance,
_propertyName = propertyName,
_object = object,
_valuesStart = {},
_valuesEnd = {},
_valuesStartRepeat = {},
_duration = 1000,
_repeat = 0,
_repeatDelayTime = nil,
_yoyo = false,
_isPlaying = false,
_reversed = false,
_delayTime = 0,
_startTime = nil,
_easingFunction = getEasingFunction("linear"),
_onStartCallback = nil,
_onStartCallbackFired = false,
_onUpdateCallback = nil,
_onCompleteCallback = nil,
_onStopCallback = nil,
_onLoopCallback = nil,
_group = group or tweenGroup.new(),
_id = nextId(),
_isPause = false,
_pauseStartTime = 0,
_pauseEndTime = 0,
_valuesStartBak = {},
_valuesEndBak = {},
_repeatBak = 0,
_yoyoCount = 0
}, Tween_mt)
end
tweenGroup =
{
}
local TweenGroup = {}
local Group_mt = {__index = TweenGroup}
function TweenGroup:getId()
return self._id
end
function TweenGroup:getAll()
return _tweens
end
function TweenGroup:removeAll()
self._tweens = {}
end
function TweenGroup:add(tween)
self._tweens[tween:getId()] = tween
self._tweensAddedDuringUpdate[tween:getId()] = tween
end
function TweenGroup:remove(tween)
self._tweens[tween:getId()] = nil
self._tweensAddedDuringUpdate[tween:getId()] = nil
end
function TweenGroup:play()
if self._isComplete == true then
self:Restart()
self._isComplete = false
self._isCompletePlay = true
return
end
for k, v in pairs(self._tweens) do
v:start()
end
end
function TweenGroup:stop()
for k, v in pairs(self._tweens) do
v:stop()
end
self._LoopCallbackCount = 0
self._CompleteCallbackCount = 0
self._isComplete = false
self._isCompletePlay = false
end
function TweenGroup:pause()
for k, v in pairs(self._tweens) do
v:pause()
end
end
function TweenGroup:update(time, preserve)
if self._isPause then
return
end
local tweenIds = table.keys(self._tweens)
if #tweenIds == 0 then
return false
end
if time == nil then
time = socket.gettime() * 1000
end
-- Tweens are updated in "batches". If you add a new tween during an update, then the
-- new tween will be updated in the next batch.
-- If you remove a tween during an update, it may or may not be updated. However,
-- if the removed tween was added during the current batch, then it will not be updated.
while #tweenIds > 0 do
self._tweensAddedDuringUpdate = {}
for i = 1, #tweenIds do
local tween = self._tweens[tweenIds[i]]
if (tween ~= nil and tween:update(time) == false) then
tween._isPlaying = false
if (preserve == false) then
self._tweens[tweenIds[i]] = nil
end
end
end
tweenIds = table.keys(self._tweensAddedDuringUpdate)
end
return
end
local function __Play(self)
self:play()
end
local function __Stop(self)
self:stop()
end
local function __Pause(self)
self:pause()
end
local function __Restart(self)
self:Stop()
self:Play()
end
local function __OnComplete(self, callback)
for k, v in pairs(self._tweens) do
v:onComplete(callback)
end
end
local function __OnLoop(self, callback)
for k, v in pairs(self._tweens) do
v:onLoop(callback)
end
end
local function __createSelfProperty()
return
{
_tweens = {},
_tweensAddedDuringUpdate = {},
_id = nextId(),
_CompleteCallbackCount = 0,
_LoopCallbackCount = 0,
_isComplete = false,
_isCompletePlay = false,
Play = __Play,
Stop = __Stop,
Pause = __Pause,
Restart = __Restart,
OnComplete = __OnComplete,
OnLoop = __OnLoop
}
end
function tweenGroup.new()
local group = {}
table.merge(group, __createSelfProperty())
return setmetatable(group, Group_mt)
end
function createTween(self, instance, tweenInfo, propertyTable)
local group = tweenGroup.new()
for k, v in pairs(propertyTable) do
if instance[k] ~= nil then
local a = {}
if type(instance[k]) == "number" then
a[k] = instance[k]
else
a = instance[k]
end
local tween = tween.new(instance, k, a, group)
:onUpdate(function(instance, propertyName, propertyValue)
if table.nums(propertyValue) == 1 then
instance[propertyName] = propertyValue[propertyName]
else
instance[propertyName] = propertyValue
end
end)
:onStop(function(instance, propertyName, propertyValue)
if table.nums(propertyValue) == 1 then
instance[propertyName] = propertyValue[propertyName]
else
instance[propertyName] = propertyValue
end
end)
if tweenInfo["duration"] ~= nil then
local b = {}
if type(v) == "number" then
b[k] = v
else
b = v
end
tween:to(b, tweenInfo["duration"])
end
if tweenInfo["repeatCount"] ~= nil then
if tweenInfo["repeatCount"] <= 0 then
tween:repeatEXT("Infinity")
else
tween:repeatEXT(tweenInfo["repeatCount"]-1)
end
end
if tweenInfo["yoyo"] ~= nil then
tween:yoyo(tweenInfo["yoyo"])
if tweenInfo["yoyo"] == true and tweenInfo["repeatCount"] ~= nil then
if tweenInfo["repeatCount"] > 0 then
local n = tweenInfo["repeatCount"] * 2 - 1
tween:repeatEXT(n)
end
end
end
if tweenInfo["easing"] ~= nil then
tween:easing(tweenInfo["easing"])
end
if tweenInfo["delay"] ~= nil then
tween:delay(tweenInfo["delay"])
end
if tweenInfo["repeatDelay"] ~= nil then
tween:repeatDelay(tweenInfo["repeatDelay"])
end
end
end
tweenGroups:add(group)
return group
end