mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Merge 38f829f169 into f3bfc65607
This commit is contained in:
commit
e5036d04bc
4 changed files with 614 additions and 67 deletions
213
src/easings.luau
Normal file
213
src/easings.luau
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
--!native
|
||||
--!optimize 2
|
||||
|
||||
export type EasingFunction = (t: number) -> number
|
||||
|
||||
local function lerp(t: number): number
|
||||
return t
|
||||
end
|
||||
|
||||
local function sine_ease_in(t: number): number
|
||||
return 1 - math.cos((math.pi * t) / 2)
|
||||
end
|
||||
|
||||
local function sine_ease_out(t: number): number
|
||||
return math.sin((math.pi * t) / 2)
|
||||
end
|
||||
|
||||
local function sine_ease_inout(t: number): number
|
||||
return -(math.cos(math.pi * t) - 1) / 2
|
||||
end
|
||||
|
||||
local function quad_ease_in(t: number): number
|
||||
return t^2
|
||||
end
|
||||
|
||||
local function quad_ease_out(t: number): number
|
||||
return 1 - (1 - t)^2
|
||||
end
|
||||
|
||||
local function quad_ease_inout(t: number): number
|
||||
return if t < 0.5 then 2 * t^2 else 1 - math.pow(-2 * t + 2, 2) / 2
|
||||
end
|
||||
|
||||
local function cubic_ease_in(t: number): number
|
||||
return t^3
|
||||
end
|
||||
|
||||
local function cubic_ease_out(t: number): number
|
||||
return 1 - math.pow(1 - t, 3);
|
||||
end
|
||||
|
||||
local function cubic_ease_inout(t: number): number
|
||||
return if t < 0.5 then 4 * t^3 else 1 - math.pow(-2 * t + 2, 3) / 2
|
||||
end
|
||||
|
||||
local function quart_ease_in(t: number): number
|
||||
return t^4
|
||||
end
|
||||
|
||||
local function quart_ease_out(t: number): number
|
||||
return 1 - math.pow(1 - t, 4)
|
||||
end
|
||||
|
||||
local function quart_ease_inout(t: number): number
|
||||
return if t < 0.5 then 8 * t^4 else 1 - math.pow(-2 * t + 2, 4) / 2
|
||||
end
|
||||
|
||||
local function quint_ease_in(t: number): number
|
||||
return t^5
|
||||
end
|
||||
|
||||
local function quint_ease_out(t: number): number
|
||||
return 1 - math.pow(1 - t, 5)
|
||||
end
|
||||
|
||||
local function quint_ease_inout(t: number): number
|
||||
return if t < 0.5 then 16 * t^5 else 1 - math.pow(-2 * t + 2, 5) / 2
|
||||
end
|
||||
|
||||
local function expo_ease_in(t: number): number
|
||||
return if t == 0 then 0 else math.pow(2, 10 * t - 10)
|
||||
end
|
||||
|
||||
local function expo_ease_out(t: number): number
|
||||
return if t == 1 then 1 else 1 - math.pow(2, -10 * t)
|
||||
end
|
||||
|
||||
local function expo_ease_inout(t: number): number
|
||||
if t == 0 then
|
||||
return 0
|
||||
elseif t == 1 then
|
||||
return 1
|
||||
end
|
||||
|
||||
return if t < 0.5
|
||||
then math.pow(2, 20 * t - 10) / 2
|
||||
else (2 - math.pow(2, -20 * t + 10)) / 2
|
||||
end
|
||||
|
||||
local function circ_ease_in(t: number): number
|
||||
return 1 - math.sqrt(1 - math.pow(t, 2))
|
||||
end
|
||||
|
||||
local function circ_ease_out(t: number): number
|
||||
return math.sqrt(1 - math.pow(t - 1, 2))
|
||||
end
|
||||
|
||||
local function circ_ease_inout(t: number): number
|
||||
return if t < 0.5
|
||||
then (1 - math.sqrt(1 - math.pow(2 * t, 2))) / 2
|
||||
else (math.sqrt(1 - math.pow(-2 * t + 2, 2)) + 1) / 2
|
||||
end
|
||||
|
||||
local _BACK_C1 = 1.70158
|
||||
local _BACK_C2 = _BACK_C1 * 1.525
|
||||
local _BACK_C3 = _BACK_C1 + 1
|
||||
local function back_ease_in(t: number): number
|
||||
return _BACK_C3 * t^3 - _BACK_C1 * t^2
|
||||
end
|
||||
|
||||
local function back_ease_out(t: number): number
|
||||
return 1 + _BACK_C3 * math.pow(t - 1, 3) + _BACK_C1 * math.pow(t - 1, 2)
|
||||
end
|
||||
|
||||
local function back_ease_inout(t: number): number
|
||||
return if t < 0.5
|
||||
then (math.pow(2 * t, 2) * ((_BACK_C2 + 1) * 2 * t - _BACK_C2)) / 2
|
||||
else (math.pow(2 * t - 2, 2) * ((_BACK_C2 + 1) * (t * 2 - 2) + _BACK_C2) + 2) / 2
|
||||
end
|
||||
|
||||
local _ELASTIC_C4 = (2 * math.pi) / 3
|
||||
local _ELASTIC_C5 = (2 * math.pi) / 4.5
|
||||
local function elastic_ease_in(t: number): number
|
||||
if t == 0 then
|
||||
return 0
|
||||
elseif t == 1 then
|
||||
return 1
|
||||
end
|
||||
|
||||
return -math.pow(2, 10 * t - 10) * math.sin((t * 10 - 10.75) * _ELASTIC_C4)
|
||||
end
|
||||
|
||||
local function elastic_ease_out(t: number): number
|
||||
if t == 0 then
|
||||
return 0
|
||||
elseif t == 1 then
|
||||
return 1
|
||||
end
|
||||
|
||||
return math.pow(2, -10 * t) * math.sin((t * 10 - 0.75) * _ELASTIC_C4) + 1
|
||||
end
|
||||
|
||||
local function elastic_ease_inout(t: number): number
|
||||
if t == 0 then
|
||||
return 0
|
||||
elseif t == 1 then
|
||||
return 1
|
||||
end
|
||||
|
||||
return if t < 0.5
|
||||
then -(math.pow(2, 20 * t - 10) * math.sin((20 * t - 11.125) * _ELASTIC_C5)) / 2
|
||||
else (math.pow(2, -20 * t + 10) * math.sin((20 * t - 11.125) * _ELASTIC_C5)) / 2 + 1
|
||||
end
|
||||
|
||||
local _BOUNCE_N1 = 7.5625
|
||||
local _BOUNCE_D1 = 2.75
|
||||
local function bounce_ease_out(t: number): number
|
||||
if t < (1 / _BOUNCE_D1) then
|
||||
return _BOUNCE_N1 * t^2
|
||||
elseif t < (2 / _BOUNCE_D1) then
|
||||
return _BOUNCE_N1 * (t - (1.5 / _BOUNCE_D1))^2 + 0.75
|
||||
elseif t < (2.5 / _BOUNCE_D1) then
|
||||
return _BOUNCE_N1 * (t - (2.25 / _BOUNCE_D1))^2 + 0.9375
|
||||
else
|
||||
return _BOUNCE_N1 * (t - (2.625 / _BOUNCE_D1))^2 + 0.984375
|
||||
end
|
||||
end
|
||||
|
||||
local function bounce_ease_in(t: number): number
|
||||
return 1 - bounce_ease_out(1 - t)
|
||||
end
|
||||
|
||||
local function bounce_ease_inout(t: number): number
|
||||
return if t < 0.5
|
||||
then (1 - bounce_ease_out(1 - 2 * t)) / 2
|
||||
else (1 + bounce_ease_out(2 * t - 1)) / 2
|
||||
end
|
||||
|
||||
local easings = {
|
||||
lerp = lerp,
|
||||
sine_ease_in = sine_ease_in,
|
||||
sine_ease_out = sine_ease_out,
|
||||
sine_ease_inout = sine_ease_inout,
|
||||
quad_ease_in = quad_ease_in,
|
||||
quad_ease_out = quad_ease_out,
|
||||
quad_ease_inout = quad_ease_inout,
|
||||
cubic_ease_in = cubic_ease_in,
|
||||
cubic_ease_out = cubic_ease_out,
|
||||
cubic_ease_inout = cubic_ease_inout,
|
||||
quart_ease_in = quart_ease_in,
|
||||
quart_ease_out = quart_ease_out,
|
||||
quart_ease_inout = quart_ease_inout,
|
||||
quint_ease_in = quint_ease_in,
|
||||
quint_ease_out = quint_ease_out,
|
||||
quint_ease_inout = quint_ease_inout,
|
||||
expo_ease_in = expo_ease_in,
|
||||
expo_ease_out = expo_ease_out,
|
||||
expo_ease_inout = expo_ease_inout,
|
||||
circ_ease_in = circ_ease_in,
|
||||
circ_ease_out = circ_ease_out,
|
||||
circ_ease_inout = circ_ease_inout,
|
||||
back_ease_in = back_ease_in,
|
||||
back_ease_out = back_ease_out,
|
||||
back_ease_inout = back_ease_inout,
|
||||
elastic_ease_in = elastic_ease_in,
|
||||
elastic_ease_out = elastic_ease_out,
|
||||
elastic_ease_inout = elastic_ease_inout,
|
||||
bounce_ease_in = bounce_ease_in,
|
||||
bounce_ease_out = bounce_ease_out,
|
||||
bounce_ease_inout = bounce_ease_inout
|
||||
}
|
||||
|
||||
return table.freeze(easings)
|
||||
139
src/lib.luau
139
src/lib.luau
|
|
@ -18,6 +18,7 @@ local show = require "./show"
|
|||
local indexes = require "./indexes"
|
||||
local values = require "./values"
|
||||
local spring, update_springs = require "./spring"()
|
||||
local tween, update_tweens = require "./tween"()
|
||||
local action = require "./action"()
|
||||
local changed = require "./changed"
|
||||
local timeout, update_timeouts = require "./timeout"()
|
||||
|
|
@ -31,92 +32,98 @@ export type Instances = create.Instances
|
|||
export type Properties<T> = create.Properties<T>
|
||||
|
||||
local function step(dt: number)
|
||||
if game then debug.profilebegin("VIDE STEP") end
|
||||
if game then debug.profilebegin("VIDE STEP") end
|
||||
|
||||
if game then debug.profilebegin("VIDE SPRING") end
|
||||
update_springs(dt)
|
||||
if game then debug.profileend() end
|
||||
if game then debug.profilebegin("VIDE SPRING") end
|
||||
update_springs(dt)
|
||||
if game then debug.profileend() end
|
||||
|
||||
if game then debug.profilebegin("VIDE SCHEDULER") end
|
||||
update_timeouts(dt)
|
||||
if game then debug.profileend() end
|
||||
-- Added Tween update loop
|
||||
if game then debug.profilebegin("VIDE TWEEN") end
|
||||
update_tweens(dt)
|
||||
if game then debug.profileend() end
|
||||
|
||||
if game then debug.profileend() end
|
||||
if game then debug.profilebegin("VIDE SCHEDULER") end
|
||||
update_timeouts(dt)
|
||||
if game then debug.profileend() end
|
||||
|
||||
if game then debug.profileend() end
|
||||
end
|
||||
|
||||
local stepped = game and game:GetService("RunService").Heartbeat:Connect(function(dt: number)
|
||||
task.defer(step, dt)
|
||||
task.defer(step, dt)
|
||||
end)
|
||||
|
||||
local vide = {
|
||||
version = version,
|
||||
version = version,
|
||||
|
||||
-- core
|
||||
root = root,
|
||||
--branch = branch,
|
||||
mount = mount,
|
||||
create = create,
|
||||
source = source,
|
||||
effect = effect,
|
||||
derive = derive,
|
||||
switch = switch,
|
||||
show = show,
|
||||
indexes = indexes,
|
||||
values = values,
|
||||
-- core
|
||||
root = root,
|
||||
--branch = branch,
|
||||
mount = mount,
|
||||
create = create,
|
||||
source = source,
|
||||
effect = effect,
|
||||
derive = derive,
|
||||
switch = switch,
|
||||
show = show,
|
||||
indexes = indexes,
|
||||
values = values,
|
||||
|
||||
-- util
|
||||
cleanup = cleanup,
|
||||
untrack = untrack,
|
||||
read = read,
|
||||
batch = batch,
|
||||
context = context,
|
||||
-- util
|
||||
cleanup = cleanup,
|
||||
untrack = untrack,
|
||||
read = read,
|
||||
batch = batch,
|
||||
context = context,
|
||||
|
||||
-- animations
|
||||
spring = spring,
|
||||
-- animations
|
||||
spring = spring,
|
||||
tween = tween,
|
||||
|
||||
-- actions
|
||||
action = action,
|
||||
changed = changed,
|
||||
-- actions
|
||||
action = action,
|
||||
changed = changed,
|
||||
|
||||
-- flags
|
||||
strict = (nil :: any) :: boolean,
|
||||
defaults = (nil :: any) :: boolean,
|
||||
defer_nested_properties = (nil :: any) :: boolean,
|
||||
-- flags
|
||||
strict = (nil :: any) :: boolean,
|
||||
defaults = (nil :: any) :: boolean,
|
||||
defer_nested_properties = (nil :: any) :: boolean,
|
||||
|
||||
-- temporary
|
||||
apply = function(instance: Instance)
|
||||
return function(props: { [any]: any })
|
||||
apply(instance, props)
|
||||
return instance
|
||||
end
|
||||
end,
|
||||
-- temporary
|
||||
apply = function(instance: Instance)
|
||||
return function(props: { [any]: any })
|
||||
apply(instance, props)
|
||||
return instance
|
||||
end
|
||||
end,
|
||||
|
||||
-- runtime
|
||||
step = function(dt: number)
|
||||
if stepped then
|
||||
stepped:Disconnect()
|
||||
stepped = nil
|
||||
end
|
||||
step(dt)
|
||||
end
|
||||
-- runtime
|
||||
step = function(dt: number)
|
||||
if stepped then
|
||||
stepped:Disconnect()
|
||||
stepped = nil
|
||||
end
|
||||
step(dt)
|
||||
end
|
||||
}
|
||||
|
||||
setmetatable(vide :: any, {
|
||||
__index = function(_, index: unknown): ()
|
||||
if flags[index] == nil then
|
||||
error(`{tostring(index)} is not a valid member of vide`, 0)
|
||||
else
|
||||
return flags[index]
|
||||
end
|
||||
end,
|
||||
__index = function(_, index: unknown): ()
|
||||
if flags[index] == nil then
|
||||
error(`{tostring(index)} is not a valid member of vide`, 0)
|
||||
else
|
||||
return flags[index]
|
||||
end
|
||||
end,
|
||||
|
||||
__newindex = function(_, index: unknown, value: unknown)
|
||||
if flags[index] == nil then
|
||||
error(`{tostring(index)} is not a valid member of vide, 0`)
|
||||
else
|
||||
flags[index] = value
|
||||
end
|
||||
end
|
||||
__newindex = function(_, index: unknown, value: unknown)
|
||||
if flags[index] == nil then
|
||||
error(`{tostring(index)} is not a valid member of vide, 0`)
|
||||
else
|
||||
flags[index] = value
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
return vide
|
||||
251
src/tween.luau
Normal file
251
src/tween.luau
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
local easings = require "./easings"
|
||||
|
||||
export type EasingStyle =
|
||||
"lerp"
|
||||
| "sine_ease_in" | "sine_ease_out" | "sine_ease_inout"
|
||||
| "quad_ease_in" | "quad_ease_out" | "quad_ease_inout"
|
||||
| "cubic_ease_in" | "cubic_ease_out" | "cubic_ease_inout"
|
||||
| "quart_ease_in" | "quart_ease_out" | "quart_ease_inout"
|
||||
| "quint_ease_in" | "quint_ease_out" | "quint_ease_inout"
|
||||
| "expo_ease_in" | "expo_ease_out" | "expo_ease_inout"
|
||||
| "circ_ease_in" | "circ_ease_out" | "circ_ease_inout"
|
||||
| "back_ease_in" | "back_ease_out" | "back_ease_inout"
|
||||
| "elastic_ease_in" | "elastic_ease_out" | "elastic_ease_inout"
|
||||
| "bounce_ease_in" | "bounce_ease_out" | "bounce_ease_inout"
|
||||
|
||||
export type TweenConfig = {
|
||||
time: number?,
|
||||
style: EasingStyle?,
|
||||
delay: number?,
|
||||
reverses: boolean?,
|
||||
repeat_count: number?,
|
||||
}
|
||||
|
||||
local graph = require "./graph"
|
||||
type Node<T> = graph.Node<T>
|
||||
type SourceNode<T> = graph.SourceNode<T>
|
||||
local create_node = graph.create_node
|
||||
local create_source_node = graph.create_source_node
|
||||
local assert_stable_scope = graph.assert_stable_scope
|
||||
local evaluate_node = graph.evaluate_node
|
||||
local update_descendants = graph.update_descendants
|
||||
local push_scope_as_child_of = graph.push_scope_as_child_of
|
||||
|
||||
type Animatable = number | CFrame | Color3 | UDim | UDim2 | Vector2 | Vector3 | Rect | { number }
|
||||
|
||||
--[[
|
||||
Unsupported datatypes:
|
||||
- bool
|
||||
- Vector2int16
|
||||
- Vector3int16
|
||||
- EnumItem
|
||||
]]
|
||||
|
||||
type TweenState<T> = {
|
||||
time: number,
|
||||
style: easings.EasingFunction,
|
||||
delay: number,
|
||||
reverses: boolean,
|
||||
repeat_count: number,
|
||||
time_passed: number,
|
||||
|
||||
x0_123: vector, x0_456: vector, -- start position
|
||||
x1_123: vector, x1_456: vector, -- target position
|
||||
|
||||
source_value: T -- current value of tween input source
|
||||
}
|
||||
|
||||
type TypeToVec6<T> = (T) -> (vector, vector)
|
||||
type Vec6ToType<T> = (vector, vector) -> T
|
||||
|
||||
local type_to_vec6 = {
|
||||
number = function(v)
|
||||
return vector.create(v, 0, 0), vector.zero
|
||||
end :: TypeToVec6<number>,
|
||||
|
||||
CFrame = function(v)
|
||||
return v.Position, vector.create(v:ToEulerAnglesXYZ())
|
||||
end :: TypeToVec6<CFrame>,
|
||||
|
||||
Color3 = function(v)
|
||||
return vector.create(v.R, v.G, v.B), vector.zero
|
||||
end :: TypeToVec6<Color3>,
|
||||
|
||||
UDim = function(v)
|
||||
return vector.create(v.Scale, v.Offset, 0), vector.zero
|
||||
end :: TypeToVec6<UDim>,
|
||||
|
||||
UDim2 = function(v)
|
||||
return vector.create(v.X.Scale, v.X.Offset, v.Y.Scale), vector.create(v.Y.Offset, 0, 0)
|
||||
end :: TypeToVec6<UDim2>,
|
||||
|
||||
Vector2 = function(v)
|
||||
return vector.create(v.X, v.Y, 0), vector.zero
|
||||
end :: TypeToVec6<Vector2>,
|
||||
|
||||
Vector3 = function(v)
|
||||
return v, vector.zero
|
||||
end :: TypeToVec6<Vector3>,
|
||||
|
||||
Rect = function(v)
|
||||
return vector.create(v.Min.X, v.Min.Y, v.Max.X), vector.create(v.Max.Y, 0, 0)
|
||||
end :: TypeToVec6<Rect>,
|
||||
|
||||
table = function(v)
|
||||
return vector.create(v[1] or 0, v[2] or 0, v[3] or 0), vector.create(v[4] or 0, 0, 0)
|
||||
end :: TypeToVec6<{ number }>
|
||||
}
|
||||
|
||||
local vec6_to_type = {
|
||||
number = function(a, b)
|
||||
return a.X
|
||||
end :: Vec6ToType<number>,
|
||||
|
||||
CFrame = function(a, b)
|
||||
return CFrame.new(a) * CFrame.fromEulerAnglesXYZ(b.X, b.Y, b.Z)
|
||||
end :: Vec6ToType<CFrame>,
|
||||
|
||||
Color3 = function(v)
|
||||
return Color3.new(math.clamp(v.X, 0, 1), math.clamp(v.Y, 0, 1), math.clamp(v.Z, 0, 1))
|
||||
end :: Vec6ToType<Color3>,
|
||||
|
||||
UDim = function(v)
|
||||
return UDim.new(v.X, math.round(v.Y))
|
||||
end :: Vec6ToType<UDim>,
|
||||
|
||||
UDim2 = function(a, b)
|
||||
return UDim2.new(a.X, math.round(a.Y), a.Z, math.round(b.X))
|
||||
end :: Vec6ToType<UDim2>,
|
||||
|
||||
Vector2 = function(v)
|
||||
return Vector2.new(v.X, v.Y)
|
||||
end :: Vec6ToType<Vector2>,
|
||||
|
||||
Vector3 = function(v)
|
||||
return v
|
||||
end :: Vec6ToType<Vector3>,
|
||||
|
||||
Rect = function(a, b)
|
||||
return Rect.new(a.X, a.Y, a.Z, b.X)
|
||||
end :: Vec6ToType<Rect>,
|
||||
|
||||
table = function(a, b)
|
||||
return { a.X, a.Y, a.Z, b.X }
|
||||
end :: Vec6ToType<{ number }>
|
||||
}
|
||||
|
||||
local invalid_type = { __index = function(_, t: string) error(`cannot tween type {t}`, 0) end }
|
||||
setmetatable(type_to_vec6, invalid_type)
|
||||
setmetatable(vec6_to_type, invalid_type)
|
||||
|
||||
local tweens: { [TweenState<unknown>]: SourceNode<unknown> } = {}
|
||||
setmetatable(tweens :: any, { __mode = "v" })
|
||||
|
||||
local function tween<T>(source: () -> T, config: TweenConfig?): () -> T
|
||||
local owner = assert_stable_scope()
|
||||
|
||||
local data: TweenState<T> = {
|
||||
time = (config and config.time) or 1,
|
||||
style = easings[(config and config.style) or "quad_ease_out"],
|
||||
delay = (config and config.delay) or 0,
|
||||
reverses = (config and config.reverses) or false,
|
||||
repeat_count = (config and config.repeat_count) or 0,
|
||||
time_passed = 0,
|
||||
x0_123 = vector.zero, x0_456 = vector.zero,
|
||||
x1_123 = vector.zero, x1_456 = vector.zero,
|
||||
source_value = false :: any,
|
||||
}
|
||||
|
||||
local output = create_source_node(false :: any)
|
||||
|
||||
local function updater_effect()
|
||||
local value = source()
|
||||
local x1_123, x1_456 = type_to_vec6[typeof(value)](value)
|
||||
|
||||
local current_val = output.cache or value
|
||||
local x0_123, x0_456 = type_to_vec6[typeof(current_val)](current_val)
|
||||
|
||||
data.x0_123, data.x0_456 = x0_123, x0_456
|
||||
data.x1_123, data.x1_456 = x1_123, x1_456
|
||||
|
||||
data.time_passed = 0
|
||||
data.source_value = value
|
||||
|
||||
tweens[data] = output
|
||||
return value
|
||||
end
|
||||
|
||||
local updater = create_node(owner, updater_effect, false :: any)
|
||||
evaluate_node(updater)
|
||||
|
||||
output.cache = data.source_value
|
||||
|
||||
return function(...)
|
||||
if select("#", ...) == 0 then
|
||||
push_scope_as_child_of(output)
|
||||
return output.cache
|
||||
end
|
||||
|
||||
local v = ... :: T
|
||||
data.x1_123, data.x1_456 = type_to_vec6[typeof(v)](v)
|
||||
data.x0_123, data.x0_456 = data.x1_123, data.x1_456
|
||||
tweens[data] = nil
|
||||
output.cache = v
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
local function update_tween_sources(dt: number)
|
||||
for data, output in tweens do
|
||||
data.time_passed += dt
|
||||
|
||||
local duration = data.time
|
||||
local delay = data.delay
|
||||
local reverses = data.reverses
|
||||
local repeat_count = data.repeat_count
|
||||
|
||||
-- The duration of one full "cycle" (forward + optional back)
|
||||
local cycle_duration = if reverses then duration * 2 else duration
|
||||
local total_time = data.time_passed - delay
|
||||
|
||||
if total_time < 0 then continue end
|
||||
|
||||
-- Calculate how many cycles have finished
|
||||
local current_cycle = math.floor(total_time / cycle_duration)
|
||||
|
||||
-- Check for completion (if not infinite repeat)
|
||||
if repeat_count ~= -1 and current_cycle > repeat_count then
|
||||
tweens[data] = nil
|
||||
output.cache = data.source_value
|
||||
else
|
||||
-- Time relative to the start of the current cycle
|
||||
local time_in_cycle = total_time % cycle_duration
|
||||
local alpha
|
||||
|
||||
if reverses then
|
||||
if time_in_cycle <= duration then
|
||||
alpha = time_in_cycle / duration
|
||||
else
|
||||
alpha = 1 - ((time_in_cycle - duration) / duration)
|
||||
end
|
||||
else
|
||||
alpha = time_in_cycle / duration
|
||||
end
|
||||
|
||||
local eased_alpha = data.style(math.clamp(alpha, 0, 1))
|
||||
|
||||
local x_123 = data.x0_123 + (data.x1_123 - data.x0_123) * eased_alpha
|
||||
local x_456 = data.x0_456 + (data.x1_456 - data.x0_456) * eased_alpha
|
||||
|
||||
output.cache = vec6_to_type[typeof(data.source_value)](x_123, x_456)
|
||||
end
|
||||
|
||||
update_descendants(output)
|
||||
end
|
||||
end
|
||||
|
||||
return function()
|
||||
return tween, function(dt: number)
|
||||
update_tween_sources(dt)
|
||||
end
|
||||
end
|
||||
76
test/tween-test.luau
Normal file
76
test/tween-test.luau
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
local vide = require "../../vide"
|
||||
|
||||
local function system(): (number) -> number
|
||||
local MAX = 40
|
||||
local MIN = 10
|
||||
|
||||
local _, input, output = vide.root(function()
|
||||
local input = vide.source(MAX)
|
||||
local output = vide.tween(input, { time = 2, style = "quad_ease_inout" })
|
||||
return input, output
|
||||
end)
|
||||
|
||||
local T = 3
|
||||
local t = 0
|
||||
return function(dt)
|
||||
t += dt
|
||||
if t >= T then
|
||||
t -= T
|
||||
input(input() == MAX and MIN or MAX)
|
||||
end
|
||||
|
||||
vide.step(dt)
|
||||
|
||||
return output()
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local function redraw_block(h: number)
|
||||
local OFFSET = 70
|
||||
|
||||
local BLOCK = "█"
|
||||
|
||||
local function remainder_to_block(x)
|
||||
return
|
||||
if x > 7/8 then "█"
|
||||
elseif x > 6/8 then "▇"
|
||||
elseif x > 5/8 then "▆"
|
||||
elseif x > 4/8 then "▅"
|
||||
elseif x > 3/8 then "▄"
|
||||
elseif x > 2/8 then "▃"
|
||||
elseif x > 1/8 then "▂"
|
||||
else "▁"
|
||||
end
|
||||
|
||||
-- math.clamp added just in case an Elastic/Back easing style overshoots below 0
|
||||
local h_f = math.max(0, math.floor(h))
|
||||
local reset = "\27[H\27[2J" -- ANSI clear terminal
|
||||
local offset = string.rep("\n", math.max(0, OFFSET - h_f))
|
||||
local bar = remainder_to_block(h - h_f) .. "\n" .. string.rep(BLOCK .. "\n", h_f)
|
||||
print(reset .. offset .. bar .. "\n" .. h)
|
||||
end
|
||||
|
||||
local program_time = os.clock()
|
||||
|
||||
local function step(): number
|
||||
local FPS = 30
|
||||
local DT = 1/FPS
|
||||
|
||||
repeat until os.clock() - program_time >= DT
|
||||
program_time += DT
|
||||
return DT
|
||||
end
|
||||
|
||||
local function loop()
|
||||
local callback = system()
|
||||
|
||||
while true do
|
||||
local dt = step()
|
||||
local x = callback(dt)
|
||||
redraw_block(x)
|
||||
end
|
||||
end
|
||||
|
||||
loop()
|
||||
Loading…
Add table
Add a link
Reference in a new issue