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:
parent
79c9f2bbd9
commit
38d2487630
2 changed files with 61 additions and 20 deletions
|
@ -1222,17 +1222,30 @@ impl ActiveThread {
|
|||
Label::new("Generating")
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small)
|
||||
.with_animation(
|
||||
.with_animations(
|
||||
"generating-label",
|
||||
Animation::new(Duration::from_secs(1)).repeat(),
|
||||
|mut label, delta| {
|
||||
let text = match delta {
|
||||
d if d < 0.25 => "Generating",
|
||||
d if d < 0.5 => "Generating.",
|
||||
d if d < 0.75 => "Generating..",
|
||||
_ => "Generating...",
|
||||
};
|
||||
label.set_text(text);
|
||||
vec![
|
||||
Animation::new(Duration::from_secs(1)),
|
||||
Animation::new(Duration::from_secs(1)).repeat(),
|
||||
],
|
||||
|mut label, animation_ix, delta| {
|
||||
match animation_ix {
|
||||
0 => {
|
||||
let chars_to_show = (delta * 10.).ceil() as usize;
|
||||
let text = &"Generating"[0..chars_to_show];
|
||||
label.set_text(text);
|
||||
}
|
||||
1 => {
|
||||
let text = match delta {
|
||||
d if d < 0.25 => "Generating",
|
||||
d if d < 0.5 => "Generating.",
|
||||
d if d < 0.75 => "Generating..",
|
||||
_ => "Generating...",
|
||||
};
|
||||
label.set_text(text);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
label
|
||||
},
|
||||
)
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue