#15 Реализовано втупую и всякие выравнивания с текстами надо добавлять вручную. Зато у нас есть поддержка анимаций и дерева матриц преобразования. Вообще UI - это просто иерархия прямоугольников на экране. Reviewed-on: #18
102 lines
3.5 KiB
Lua
102 lines
3.5 KiB
Lua
local easing = require "lib.utils.easing"
|
||
|
||
--- @alias voidCallback fun(): nil
|
||
--- @alias animationRunner fun(node: AnimationNode)
|
||
|
||
--- Узел дерева анимаций.
|
||
---
|
||
--- Отслеживает завершение всех анимаций всех дочерних узлов и оповещает вышестоящий узел.
|
||
---
|
||
--- Дочерние узлы одного уровня запускают свою анимацию одновременно после завершения анимации родителя.
|
||
--- Example:
|
||
--- ```lua
|
||
--- AnimationNode {
|
||
--- function (node) residentsleeper:sleep(1000, node) end, -- must pass itself down as the parameter
|
||
--- onEnd = function() print("completed") end,
|
||
--- children = { -- children run in parallel after the parent animation is completed
|
||
--- AnimationNode {
|
||
--- function (node) sprite:animate("attack", node) end
|
||
--- },
|
||
--- AnimationNode {
|
||
--- function (node) other_sprite:animate("hurt", node) end
|
||
--- },
|
||
--- }
|
||
--- }:run()
|
||
--- ```
|
||
--- @class AnimationNode
|
||
--- @field count integer
|
||
--- @field run animationRunner
|
||
--- @field parent AnimationNode?
|
||
--- @field children AnimationNode[]
|
||
--- @field finish voidCallback
|
||
--- @field onEnd voidCallback?
|
||
--- @field duration number продолжительность в миллисекундах
|
||
--- @field easing ease функция смягчения
|
||
--- @field t number прогресс анимации
|
||
--- @field finished boolean
|
||
local animation = {}
|
||
animation.__index = animation
|
||
|
||
--- Регистрация завершения дочерней анимации
|
||
function animation:bubbleUp()
|
||
self.count = self.count - 1
|
||
if self.count > 0 then return end
|
||
self.finished = true
|
||
if self.onEnd then self.onEnd() end
|
||
if self.parent then self.parent:bubbleUp() end
|
||
end
|
||
|
||
--- @param children AnimationNode[]
|
||
--- Запланировать анимации после текущей, которые запустятся одновременно друг с другом
|
||
function animation:chain(children)
|
||
for _, child in ipairs(children) do
|
||
child.parent = self
|
||
table.insert(self.children, child)
|
||
self.count = self.count + 1
|
||
end
|
||
return self
|
||
end
|
||
|
||
--- Возвращает текущий прогресс анимации с учетом смягчения
|
||
function animation:getValue()
|
||
return self.easing(self.t)
|
||
end
|
||
|
||
function animation:update(dt)
|
||
if self.finished then return end
|
||
|
||
if self.t < 1 then
|
||
self.t = self.t + dt * 1000 / self.duration -- в знаменателе продолжительность анимации в секундах
|
||
else
|
||
self.t = 1
|
||
self:finish()
|
||
end
|
||
end
|
||
|
||
--- @param data {[1]: animationRunner?, onEnd?: voidCallback, duration: number?, easing: ease?, children?: AnimationNode[]}
|
||
--- @return AnimationNode
|
||
local function new(data)
|
||
local t = setmetatable({}, animation)
|
||
t.run = data[1] or function(self)
|
||
self:finish()
|
||
end
|
||
t.onEnd = data.onEnd
|
||
t.count = 1 -- своя анимация
|
||
t.finished = false
|
||
t.children = {}
|
||
t:chain(data.children or {})
|
||
t.duration = data.duration or 1000
|
||
t.easing = data.easing or easing.linear
|
||
t.t = 0
|
||
t.finish = function()
|
||
if t.finished then return end
|
||
t:bubbleUp()
|
||
for _, anim in ipairs(t.children) do
|
||
anim:run()
|
||
end
|
||
end
|
||
return t
|
||
end
|
||
|
||
return new
|