heroes-of-nerevelon/lib/animation_node.lua
PeaAshMeter 538bd1df33 feature/simple_ui (#18)
#15
Реализовано втупую и всякие выравнивания с текстами надо добавлять вручную.
Зато у нас есть поддержка анимаций и дерева матриц преобразования.
Вообще UI - это просто иерархия прямоугольников на экране.

Reviewed-on: #18
2025-11-08 01:32:46 +03:00

102 lines
3.5 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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