agent: Polish Generating... animation (#28379)

https://github.com/user-attachments/assets/9e798a50-9403-4e1c-a3df-2931e748b77d



Release Notes:

- N/A
This commit is contained in:
Bennet Bo Fenner 2025-04-08 18:14:30 -06:00 committed by GitHub
parent 79c9f2bbd9
commit 38d2487630
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 61 additions and 20 deletions

View file

@ -3,6 +3,7 @@ use std::time::{Duration, Instant};
use crate::{AnyElement, App, Element, ElementId, GlobalElementId, IntoElement, Window};
pub use easing::*;
use smallvec::SmallVec;
/// An animation that can be applied to an element.
pub struct Animation {
@ -50,6 +51,24 @@ pub trait AnimationExt {
animation: Animation,
animator: impl Fn(Self, f32) -> Self + 'static,
) -> AnimationElement<Self>
where
Self: Sized,
{
AnimationElement {
id: id.into(),
element: Some(self),
animator: Box::new(move |this, _, value| animator(this, value)),
animations: smallvec::smallvec![animation],
}
}
/// Render this component or element with a chain of animations
fn with_animations(
self,
id: impl Into<ElementId>,
animations: Vec<Animation>,
animator: impl Fn(Self, usize, f32) -> Self + 'static,
) -> AnimationElement<Self>
where
Self: Sized,
{
@ -57,7 +76,7 @@ pub trait AnimationExt {
id: id.into(),
element: Some(self),
animator: Box::new(animator),
animation,
animations: animations.into(),
}
}
}
@ -68,8 +87,8 @@ impl<E> AnimationExt for E {}
pub struct AnimationElement<E> {
id: ElementId,
element: Option<E>,
animation: Animation,
animator: Box<dyn Fn(E, f32) -> E + 'static>,
animations: SmallVec<[Animation; 1]>,
animator: Box<dyn Fn(E, usize, f32) -> E + 'static>,
}
impl<E> AnimationElement<E> {
@ -91,6 +110,7 @@ impl<E: IntoElement + 'static> IntoElement for AnimationElement<E> {
struct AnimationState {
start: Instant,
animation_ix: usize,
}
impl<E: IntoElement + 'static> Element for AnimationElement<E> {
@ -108,22 +128,30 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
cx: &mut App,
) -> (crate::LayoutId, Self::RequestLayoutState) {
window.with_element_state(global_id.unwrap(), |state, window| {
let state = state.unwrap_or_else(|| AnimationState {
let mut state = state.unwrap_or_else(|| AnimationState {
start: Instant::now(),
animation_ix: 0,
});
let mut delta =
state.start.elapsed().as_secs_f32() / self.animation.duration.as_secs_f32();
let animation_ix = state.animation_ix;
let mut delta = state.start.elapsed().as_secs_f32()
/ self.animations[animation_ix].duration.as_secs_f32();
let mut done = false;
if delta > 1.0 {
if self.animation.oneshot {
done = true;
if self.animations[animation_ix].oneshot {
if animation_ix >= self.animations.len() - 1 {
done = true;
} else {
state.start = Instant::now();
state.animation_ix += 1;
}
delta = 1.0;
} else {
delta %= 1.0;
}
}
let delta = (self.animation.easing)(delta);
let delta = (self.animations[animation_ix].easing)(delta);
debug_assert!(
(0.0..=1.0).contains(&delta),
@ -131,7 +159,7 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
);
let element = self.element.take().expect("should only be called once");
let mut element = (self.animator)(element, delta).into_any_element();
let mut element = (self.animator)(element, animation_ix, delta).into_any_element();
if !done {
window.request_animation_frame();