Fusion icon indicating copy to clipboard operation
Fusion copied to clipboard

Animation timelines/sequences/keyframes

Open dphfox opened this issue 3 years ago • 8 comments

Right now, it can be awkward to work with animations in Fusion that involve multiple keyframes or values.

Fusion could implement a Timeline object which allows the user to define 'keyframes' for a value, which can then be moved/interpolated between by some state representing the current time:

local currentTime = State(0)

local colour = Timeline(currentTime, {
  [0] = Color3.new(1, 0, 0)
  [0.5] = Color3.new(0, 1, 0)
  [1] = Color3.new(0, 0, 1)
})

print(colour:get()) -- red

currentTime:set(0.5)
print(colour:get()) -- green

currentTime:set(0.75)
print(colour:get()) -- halfway between green and blue

currentTime:set(1)
print(colour:get()) --blue

This would be useful for defining more complex declarative animations in Fusion.

dphfox avatar Aug 11 '21 16:08 dphfox

For reference, the above code might currently look like this today:

local currentTime = State(0)

local RED = Color3.new(1, 0, 0)
local GREEN = Color3.new(0, 1, 0)
local BLUE = Color3.new(0, 0, 1)

local color = Computed(function()
    local t = currentTime:get()
    if t <= 0 then
        return RED
    elseif t <= 0.5 then
        return RED:Lerp(GREEN, t*2)
    elseif t <= 1 then
        return GREEN:Lerp(BLUE, t*2 - 1)
    else  
        return BLUE
    end
end)

Also worth mentioning Fusion can also provide perceptually uniform colour blending (Oklab) which looks better than default Roblox lerping (sRGB)

dphfox avatar Aug 11 '21 16:08 dphfox

Plus, this could even be used beyond just animation - because this is all done with state objects, you can use it to process any kind of data you'd like!

Here's an example where you can use it to map values from one range (50-100) to another (0-1):

local input = State(50)

local output = Timeline(input, {[50] = 0, [100] = 1})

print(input:get(), "=", output:get()) -- 50 = 0

input:set(75)
print(input:get(), "=", output:get()) -- 75 = 0.5

input:set(100)
print(input:get(), "=", output:get()) -- 100 = 1

dphfox avatar Aug 11 '21 16:08 dphfox

Comparing the code with the Timeline component to the one without it, I definitely think that the Timeline component will be a great addition to Fusion. Is there a way to "tween" the currentTime value?

BPilot253 avatar Aug 11 '21 17:08 BPilot253

Yup - using a Tween or Spring object:

local currentTime = State(0)

local colour = Timeline(
  Tween(currentTime, TweenInfo.new(2, Enum.EasingStyle.Linear, Enum.EasingDirection.Out)), 
  {
    [0] = Color3.new(1, 0, 0)
    [0.5] = Color3.new(0, 1, 0)
    [1] = Color3.new(0, 0, 1)
  }
)

dphfox avatar Aug 11 '21 17:08 dphfox

Yup - using a Tween or Spring object:


local currentTime = State(0)



local colour = Timeline(

  Tween(currentTime, TweenInfo.new(2, Enum.EasingStyle.Linear, Enum.EasingDirection.Out)), 

  {

    [0] = Color3.new(1, 0, 0)

    [0.5] = Color3.new(0, 1, 0)

    [1] = Color3.new(0, 0, 1)

  }

)

Then that's perfect!

BPilot253 avatar Aug 11 '21 17:08 BPilot253

Could also be useful in conjunction with timers: https://github.com/Elttob/Fusion/issues/12

dphfox avatar Aug 11 '21 17:08 dphfox

Another thing to consider is 'extrapolation behaviour' - what if you pass in a value before the start of the timeline, or after the end?

For animation uses, clamping to the nearest value would be good enough, but perhaps for some uses it'd be worth providing an option to allow for extrapolation?

dphfox avatar Aug 11 '21 18:08 dphfox

Perhaps 'Keyframes' would be a more appropriate name for this API?

dphfox avatar Aug 30 '21 09:08 dphfox