While working on some other projects that involved some UI, I decided to create an animation library for Roact. I’ve been implementing it while using it in another project. Now, I feel like it’s in a good enough state to share it with the world.
The source code is available on my gitlab: https://gitlab.com/seaofvoices/flick
You can also grab the Roblox model file on the release page: https://gitlab.com/seaofvoices/flick/-/releases
I have not written a documentation site yet but if you are giving it a try, feel free to ping me on discord if you have any questions.
I’ll end this post with some kind of example/API reference in you want to try it!
Happy developing
How It Works
Provider
Flick works using a Roact context, so you need to wrap the top level component with the engine provider.
local function App(props)
return Roact.createElement(Flick.Provider, {
value = Flick.Engine.new(),
}, {
ChildComponent = Roact.createElement(SomeComponent),
})
end
withAnimation
Animate your components by using Flick.withAnimation
. This will pass a new prop to your component named flick
, that let’s you trigger animations.
Pass the different values you want to animate in the initialValues
table. This table must map strings to a value that flick is able to animate. Right now, flick supports these types:
- number
- Color3
- Vector2
- Vector3
- UDim
- UDim2
Example
An animated button, that changes the color on hover and while it’s pressed:
local function Button(props)
local flick = props.flick
local text = props.text
local onClick = props.onClick
return Roact.createElement('TextButton', {
AutoButtonColor = false,
Text = 'Click'
-- here we assign using the name of the generated binding
BackgroundColor3 = flick.bindings.foo,
[Roact.Event.MouseButton1Click] = onClick,
[Roact.Event.MouseEnter] = function()
props.flick:setGoals({
foo = Color3.new(0.75, 0.75, 0.75),
})
end,
[Roact.Event.MouseLeave] = function()
props.flick:resetGoals()
end,
[Roact.Event.MouseButton1Down] = function()
props.flick:setGoals({
foo = Color3.new(0.5, 0.5, 0.5),
})
end,
[Roact.Event.MouseButton1Up] = function()
props.flick:resetGoals()
end,
})
end
local AnimatedButton = Flick.withAnimation({
initialValues = {
foo = Color3.new(1, 1, 1),
},
motion = Flick.Motions.Timed(0.15),
})(Button)
return AnimatedButton
API
Just to avoid any confusion, the library itself is referred as Flick
, where the injected property you get into the animated component is referred as flick
flick prop
flick.bindings
This table contains all the bindings generated by the library for you. For each entry you have in the initialValues
given to withAnimation
, you’ll have a binding named the same.
Example
If you wrap MyComponent
with this configuration
return Flick.withAnimation({
initialValues = {
color = Color3.new(1, 1, 1),
transparency = 0,
},
motion = Flick.Motions.Timed(0.15),
})(MyComponent)
You can assign the two bindings to the BackgroundColor property and the BackgroundTransparency
BackgroundColor = flick.bindings.color,
BackgroundTransparency = flick.bindings.transparency,
flick:setGoals(goals, goalReached: (string) -> ())
Animates each given goal to its associated value.
Pass the table that maps the new values you want to reach. You don’t have to specify all the values, simply put those you want to animate to. With the previous example, you could do
flick:setGoals({
transparency = 1,
})
flick:setGoals({
color = Color3.new(0.25, 0.25, 0.25),
})
flick:setGoals({
transparency = 1,
color = Color3.new(0.25, 0.25, 0.25),
})
The callback is called whenever one goal is reached. It receives the goal name.
flick:resetGoals(reached: (string) -> ())
Works like setGoals
, except that it animates all values to their initial value (given using withAnimation
).
flick:jumpToGoals(goals)
Works like setGoals
, except that it does not animate, it goes directly to the given values.
Flick.Motions
Motions are used to configure how your animations will animate. For now, there is only one option which is the timed motion.
Flick.Motions.Timed(duration: number | config)
where config = { duration: number, easing: 'quad' | 'linear' }
Flick.Motions.Timed(0.2) -- give the duration and easing is defaulted to 'quad'
Flick.Motions.Timed({
duration = 0.2,
easing = 'quad',
})
Flick.withAnimation(config) -> (Component) -> Component
where config: { initialValues: table, motion: Flick.Motions }
The type is just returned might look confusing, but it just returns a function that wraps the given component with the configuration you passed to withAnimation
.
local AnimatedButton = Flick.withAnimation({
initialValues = {
foo = Color3.new(1, 1, 1),
},
motion = Flick.Motions.Timed(0.15),
})(Button)
When you’ll render the AnimatedButton
, you don’t have to pass the flick
prop. It will automatically get it, merge it to the other props and pass them down to Button
.