transition widget#3344
Conversation
|
Made some changes:
It's a bit unfortunate that |
Thanks to #3348, this is no longer the case! |
|
This is looking good. One of the advantages of a custom widget like this is that we may be able to trigger application-wide layout invalidation only when the contents change sizing strategy. This should make small local animations a lot more efficient, as they will only trigger a local relayout and a redraw. It's a bit tricky to get it right, however. But let me cook. |
|
Sounds good 👍, I'll leave the rest to you. |
|
I cooked something in 597aa3b. It isn't pretty, but it should get the job done; hopefully. |
This pr introduces a new
transitionwidget to help make animated views much more ergonomic to implement.The current approach to adding animations requires storing some
Animation<T>in app state, subscribing to window redraws viawindow::framesand then updating the animation.Furthermore, since interpolating a value also requires
Instant, if you're not usingapplication::timed, it's fairly common to storeInstantin app state, often-times in multiple places, or even bundled together withAnimation<T>in another struct.The plumbing can get quite cumbersome if you want to animate several parts of your app. It's not the kind that could be made easier with helper functions.
API Rundown
The
transitionfunction is quite simple:init- A closure to initialize the animation.target_value- the value the animation will transition to.view- A closure that takesAnimationandInstantto produce an element. This gets called every frame until the animation is complete.Example
A
transitionwidget can be used to make theprogress_barsmooth:smooth-progress-bar.mp4
Grouping Multiple Animations
Eventually, there will be a point where we may want to apply several animations to different parts of the same widget/view. You could nest multiple
transitionwidgets, but that gets horrible quickly.To remediate this, I've introduced a
transition::groupedhelper.The API is the same as before:
The only difference is that
initcan return any type so long as it implementstransition::Program:It's the implementor's responsibility to ensure that every animation used is updated properly. Consequently, this approach makes it fairly easy to forget to call
.go_mutand.is_animatingfor every animation we add.Admittedly, this part could do with more work. We could explore using closures for
tickandis_animatingto permit borrowing from app state instead of traits.Example
Let's update our smooth progress bar so that it turns green when it reaches 100% and let's also make it pulse.
updated-progress-bar.mp4
Resetting an Animation
The animation can be reset under 3 circumstances:
.key(value)is added, andvaluechanges.transition::reset(id)to the runtime, given that the widget is given matching IDs.I personally haven't found a use case where I would need to explicitly reset the internal state, but since we no longer directly control the Animation, this could be useful.
Limitations (so far)
Disabledwhen it's animated since they don't storeStatusin the widget tree.Testing
I haven't done anything non-trivial with this widget yet, so there's plenty of room for more testing and exploration.