From a00e61ff0db30e5561cb2c2cae3800ba0a8a43d1 Mon Sep 17 00:00:00 2001 From: Amber Grace <131925693+AmberGraceSoftware@users.noreply.github.com> Date: Mon, 9 Dec 2024 09:00:43 -0700 Subject: [PATCH] StateAnimation API proposal --- proposals/StateAnimation.md | 72 +++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 proposals/StateAnimation.md diff --git a/proposals/StateAnimation.md b/proposals/StateAnimation.md new file mode 100644 index 0000000..a3be883 --- /dev/null +++ b/proposals/StateAnimation.md @@ -0,0 +1,72 @@ +# StateAnimation + +StateAnimation is a simple construct designed to "proceduralize" animations in Dex. Often times, procedural code is a much easier way to conceptualize, implement, and reason about animations. + +The name "StateAnimation" implies that you are "animating a state", allowing Dex's observable abstraction patterns to shine, and separating concerns in a neat way. + +## Constructor + +The StateAnimation constructor takes in the following parameters: +```lua +Dex.StateAnimation( + defaultState: T, + animation: (setState: (T) -> (), previousState: T, ...PlayArgs) -> () +) -> Dex.StateAnimation +``` +Which is inherits `Dex.Observable` + +Example: +```luau +-- StateAnimation takes in an initial state, and a function generator. +-- The animation thread is cancelled via `task.cancel`, allowing yielding +-- within the animation function. +local fadeAndSlideIn = Dex.StateAnimation( + { + Position = Vector2.new(0.5, 0.5), + Transparency = 1 + }, + function(setState, previousState, goalPoint: Vector2) + local startPos = initialState.Position + local startTrans = initialState.Transparency + local goalTrans = 0 + + local alpha = 0 + while alpha < 1 do + alpha += RunService.RenderStepped:Wait() / 3 + alpha = math.clamp(alpha, 0, 1) + local alphaSinCurved = TweenService:GetValue( + alpha, + Enum.EasingStyle.Sine, + Enum.EasingDirection.InOut + ) + setState({ + Transparency = startTrans + (goalTrans - startTrans) * alpha, + Position = startPos + (goalPos - startPos) * alphaSinCurved + }) + end + end, +end) + +-- "fadeAndSlideIn" can be played, paused, and stop at any time +-- (e.g. on component mount) +fadeAndSlideIn:Play(Vector2.new(0.75, 0.9)) +task.delay(2, function() + fadeAndSlideIn:Pause() + task.wait(2) + fadeAndSlideIn:Stop() +end) + +-- The state animation can be re-mapped to various properties on a +-- VirtualInstance. +local vInst = Dex.Premade("Frame", { + BackgroundTransparency = fadeAndSlideIn:Map(function(state) + return state.Transparency, + end), + Position = fadeAndSlideIn:Map(function(state) + return state.Position, + end), + AnchorPoint = fadeAndSlideIn:Map(function(state) + return Vector2.one - state.Position, + end), +}) +```