@@ -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();