I’m exploring how animation might work if they were added to iced, taking some inspiration from the ease of use of SwiftUI animations & this PR.
I’m particularly interested in interruptibility & ease of use. I’ve written an independent animation model that can be used to interpolate state transitions with proper interruption & timing curves.
The rough draft animation struct interface looks like this.
/// Advances the animation & returns true if redraw is needed
pub fn tick(&mut self, time: Time) -> bool;
/// Returns the state of the animation with the timing curve applied
pub fn timed_progress(&self) -> Value;
/// Begins an animation to a new state, interrupting the animation in-place if necessary
pub fn transition(&mut self, destination: Value, time: Time);
/// New animation
pub fn new(position: Value, duration: f32, timing: Timing) -> Self;
I’d like to put some more consideration into exactly how animation axes are separated & speed vs duration for constant / non-constant ranges (true / false being a constant range, 100.0 to 200.0 could change range).
At the moment an Animation object handles any number of scalars that will share timing - so if you want a quick hover animation and a slow on / off animation you will need two animation models. Maybe that should be bundled into one object? TBD.
Now, how to integrate it well? I’ve considered a couple things.
For a simple approach: iced could let the caller store animation state in their view models with AnimationUpdated(Animation<T>),
Message callback. This would require views that normally take booleans to be able to take floats or the animation model. Every view supporting animation also now needs to handle it’s own redraw requests which is not ideal.
For a more involved approach: iced provides some container component to intercept the state, trigger transitions when state changes, schedule redraws, store internal animation state & hand down the interpolated value(s) to the view it contains.
Here’s what that could look like in use.
let animator = Animator::new(
(self.on.animatable()),
std::time::Duration::from_millis(500),
Timing::EaseInOut,
|on_amount| {
toggle(
on_amount, // A float instead of a boolean
Message::Toggle,
)
.into()
},
);
I have this approach drafted & functional for interpolated drawing code, not as much for interpolating layout.
I imagine there is an even more involved approach where every view checks it’s state through some interface that will return interpolated values whenever necessary.
Let me know your thoughts! I wonder if iced is in a state where this is worth digging into further & whether anyone here has any recommendations on how this may best fit into the iced system.
Thanks
In the meantime, check it out in action: