Tweens
While Springs are perfect for fluid, physics-driven interactions, sometimes you need an animation to take exactly a specific amount of time, or follow a highly specific easing curve.
Traditional Roblox development relies on TweenService to handle this. However, imperative Tween:Play() and Tween:Cancel() calls don't mix well with a declarative UI framework. Flux provides a completely reactive wrapper around tweens, allowing you to bind them directly to your UI properties just like standard Values.
Basic Usage
You create a reactive tween using Flux.tween().
The function takes two arguments:
- Target: The value the tween should animate toward.
- TweenInfo (Optional): A standard Roblox
TweenInfoobject that dictates the duration, easing style, and easing direction. If omitted, it defaults to0.3seconds withCubicInOuteasing.
The resulting object acts exactly like a standard Flux Value, meaning it can be bound directly to UI properties.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Flux = require(ReplicatedStorage.Flux)
local new = Flux.new
-- Create a tween starting at 0, using a 1-second Linear tween
local loadingProgress = Flux.tween(
0,
TweenInfo.new(1, Enum.EasingStyle.Linear)
)
local progressBar = new "Frame" {
BackgroundColor3 = Color3.fromRGB(0, 255, 100),
-- Bind the tweened value directly to the size
Size = function()
return UDim2.fromScale(loadingProgress(), 1)
end
}
-- Later: Update the target to 1 (100%). It will tween there over 1 second.
loadingProgress(1)Reactive Targets
Just like Springs, the true power of Flux Tweens comes from the reactive graph. If you pass an existing Flux Value or Computed into the Flux.tween constructor, the tween will automatically track it.
Whenever that underlying state changes, the tween instantly calculates the difference and begins animating toward the new target from its current position.
local isHovering = Flux(false)
-- The target is a Computed Node based on hover state
local buttonColor = Flux.tween(
Flux(function()
return isHovering() and Color3.fromRGB(80, 80, 80) or Color3.fromRGB(45, 45, 45)
end),
TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
)
local myButton = new "TextButton" {
Size = UDim2.fromOffset(200, 50),
-- Bind the reactive tween
BackgroundColor3 = buttonColor,
-- Mutate the underlying state
__EVENT = {
MouseEnter = function() isHovering(true) end,
MouseLeave = function() isHovering(false) end
}
}Reactive TweenInfo
In highly dynamic systems, you might want the animation duration or easing style to change based on the state of the game. Flux supports passing a reactive Node for the TweenInfo argument as well.
If the underlying TweenInfo node updates, the tween will immediately adopt the new duration and easing parameters for all future target updates.
local isFastMode = Flux(false)
local dynamicTweenInfo = Flux(function()
if isFastMode() then
return TweenInfo.new(0.1, Enum.EasingStyle.Linear)
else
return TweenInfo.new(0.5, Enum.EasingStyle.Exponential)
end
end)
-- The tween will adapt its speed based on `isFastMode`
local slidingPosition = Flux.tween(targetPositionNode, dynamicTweenInfo)Supported Data Types
Under the hood, Flux handles the complex math required to interpolate properties automatically. You can safely tween almost any standard Roblox data type without manually splitting axes or calculating alphas.
Supported types include:
numberUDim&UDim2Vector2,Vector3,Vector2int16,Vector3int16Color3CFrameNumberRange,NumberSequenceKeypoint,ColorSequenceKeypointRect,Ray,PhysicalProperties,DateTime
🎨 Oklab Color Interpolation
Just like Springs, when you tween a Color3 value, Flux automatically bypasses standard linear RGB interpolation. It converts your colors into the Oklab perceptual color space during the animation. This prevents the "muddy" or "gray" intermediate colors that normally happen when tweening between contrasting hues in Roblox, resulting in vibrant, visually pleasing transitions.