1use parking_lot::RwLock;
2use std::sync::OnceLock;
3use web_time::{Duration, Instant};
4
5pub(crate) fn now() -> Instant {
6 let lock = CLOCK.get_or_init(|| RwLock::new(Box::new(SystemClock) as Box<dyn Clock>));
7 lock.read().now()
8}
9
10#[derive(Clone, Copy, Debug)]
12pub struct SpringSpec {
13 pub damping_ratio: f32,
16 pub stiffness: f32,
18 pub settle_progress: f32,
21 pub settle_velocity: f32,
23}
24
25impl SpringSpec {
26 pub const fn new(damping_ratio: f32, stiffness: f32) -> Self {
27 Self {
28 damping_ratio,
29 stiffness,
30 settle_progress: 0.005,
31 settle_velocity: 0.1,
32 }
33 }
34 pub const fn gentle() -> Self {
36 Self::new(0.5, 200.0)
37 }
38 pub const fn bouncy() -> Self {
40 Self::new(0.2, 300.0)
41 }
42 pub const fn crit() -> Self {
44 Self::new(1.0, 200.0)
45 }
46 pub const fn stiff() -> Self {
48 Self::new(0.8, 600.0)
49 }
50
51 pub const fn with_settle_progress(mut self, threshold: f32) -> Self {
54 self.settle_progress = threshold;
55 self
56 }
57
58 pub const fn with_settle_velocity(mut self, threshold: f32) -> Self {
60 self.settle_velocity = threshold;
61 self
62 }
63}
64
65#[derive(Clone, Copy, Debug)]
68pub struct CubicBezier {
69 pub p1x: f32,
70 pub p1y: f32,
71 pub p2x: f32,
72 pub p2y: f32,
73}
74
75impl CubicBezier {
76 pub const fn new(p1x: f32, p1y: f32, p2x: f32, p2y: f32) -> Self {
77 Self { p1x, p1y, p2x, p2y }
78 }
79}
80
81pub const EASING_EMPHASIZED_DECELERATE: CubicBezier = CubicBezier::new(0.05, 0.7, 0.1, 1.0);
83pub const EASING_STANDARD_DECELERATE: CubicBezier = CubicBezier::new(0.2, 0.0, 0.0, 1.0);
85
86#[derive(Clone, Copy, Debug)]
87#[non_exhaustive]
88pub enum Easing {
89 Linear,
90 EaseIn,
91 EaseOut,
92 EaseInOut,
93 SpringCrit {
95 omega: f32,
96 },
97 SpringGentle,
99 SpringBouncy,
101 FastOutSlowIn,
104 Custom(CubicBezier),
106}
107
108impl Easing {
109 pub fn interpolate(&self, t: f32) -> f32 {
110 match self {
111 Easing::Linear => t,
112 Easing::EaseIn => t * t,
113 Easing::EaseOut => t * (2.0 - t),
114 Easing::EaseInOut => {
115 if t < 0.5 {
116 2.0 * t * t
117 } else {
118 -1.0 + (4.0 - 2.0 * t) * t
119 }
120 }
121 Easing::SpringCrit { omega } => {
122 let w = (*omega).max(0.0);
123 let tt = t.max(0.0);
124 1.0 - (1.0 + w * tt) * (-(w * tt)).exp()
126 }
127 Easing::SpringGentle => spring_underdamped_normalized(t, 0.5, 8.0),
128 Easing::SpringBouncy => spring_underdamped_normalized(t, 0.2, 12.0),
129 Easing::FastOutSlowIn => eval_cubic_bezier(0.4, 0.0, 0.2, 1.0, t),
130 Easing::Custom(cb) => eval_cubic_bezier(cb.p1x, cb.p1y, cb.p2x, cb.p2y, t),
131 }
132 }
133}
134
135fn eval_cubic_bezier(p1x: f32, p1y: f32, p2x: f32, p2y: f32, t: f32) -> f32 {
139 let t = t.clamp(0.0, 1.0);
140 if t <= 0.0 {
141 return 0.0;
142 }
143 if t >= 1.0 {
144 return 1.0;
145 }
146 let mut u = t;
147 for _ in 0..6 {
148 let omu = 1.0 - u;
149 let x = 3.0 * omu * omu * u * p1x + 3.0 * omu * u * u * p2x + u * u * u;
150 let dx = 3.0 * omu * omu * p1x + 6.0 * omu * u * (p2x - p1x) + 3.0 * u * u * (1.0 - p2x);
151 if dx.abs() < 1e-10 {
152 break;
153 }
154 u -= (x - t) / dx;
155 u = u.clamp(0.0, 1.0);
156 }
157 let omu = 1.0 - u;
158 3.0 * omu * omu * u * p1y + 3.0 * omu * u * u * p2y + u * u * u
159}
160
161fn hermite_interpolate(h: f32, x: f32, y1: f32, y2: f32, t1: f32, t2: f32) -> f32 {
167 let x2 = x * x;
168 let x3 = x2 * x;
169 h * t1 * (x - 2.0 * x2 + x3) + h * t2 * (x3 - x2) + y1 - (3.0 * x2 - 2.0 * x3) * (y1 - y2)
170}
171
172#[allow(dead_code)]
174fn hermite_differential(h: f32, x: f32, y1: f32, y2: f32, t1: f32, t2: f32) -> f32 {
175 let x2 = x * x;
176 h * (t1 - 2.0 * x * (2.0 * t1 + t2) + 3.0 * (t1 + t2) * x2) - 6.0 * (x - x2) * (y1 - y2)
177}
178
179#[derive(Clone, Debug)]
184pub struct MonoSpline {
185 times: Vec<f32>,
186 values: Vec<f32>,
187 tangents: Vec<f32>,
188}
189
190impl MonoSpline {
191 pub fn new(times: Vec<f32>, values: Vec<f32>) -> Self {
195 assert!(times.len() >= 2, "MonoSpline requires at least 2 keyframes");
196 assert_eq!(times.len(), values.len());
197 let n = times.len();
198 let mut tangents = vec![0.0; n];
199
200 let mut slopes = vec![0.0; n.saturating_sub(1)];
202 for i in 0..n - 1 {
203 let dt = times[i + 1] - times[i];
204 slopes[i] = (values[i + 1] - values[i]) / dt;
205 }
206
207 tangents[0] = slopes[0];
209 for i in 1..n - 1 {
210 tangents[i] = (slopes[i - 1] + slopes[i]) * 0.5;
211 }
212 tangents[n - 1] = slopes[n - 2];
213
214 for i in 0..n - 1 {
216 if slopes[i] == 0.0 {
217 tangents[i] = 0.0;
218 tangents[i + 1] = 0.0;
219 } else {
220 let a = tangents[i] / slopes[i];
221 let b = tangents[i + 1] / slopes[i];
222 let h = (a * a + b * b).sqrt();
223 if h > 9.0 {
224 let t = 3.0 / h;
225 tangents[i] = t * a * slopes[i];
226 tangents[i + 1] = t * b * slopes[i];
227 }
228 }
229 }
230
231 Self {
232 times,
233 values,
234 tangents,
235 }
236 }
237
238 pub fn evaluate(&self, t: f32) -> f32 {
241 let n = self.times.len();
242 let first = self.times[0];
243 let last = self.times[n - 1];
244
245 if t <= first {
246 return self.values[0] + (t - first) * self.tangents[0];
247 }
248 if t >= last {
249 return self.values[n - 1] + (t - last) * self.tangents[n - 1];
250 }
251
252 for i in 0..n - 1 {
253 if t >= self.times[i] && t <= self.times[i + 1] {
254 let h = self.times[i + 1] - self.times[i];
255 let x = (t - self.times[i]) / h;
256 return hermite_interpolate(
257 h,
258 x,
259 self.values[i],
260 self.values[i + 1],
261 self.tangents[i],
262 self.tangents[i + 1],
263 );
264 }
265 }
266
267 self.values[n - 1] }
269}
270
271fn spring_analytical(zeta: f32, stiffness: f32, t: f32, x0: f32, v0: f32) -> (f32, f32) {
273 if t <= 0.0 {
274 return (x0, v0);
275 }
276
277 let omega = if stiffness > 0.0 {
278 stiffness.sqrt()
279 } else {
280 return (x0 + v0 * t, v0);
281 };
282
283 let zeta = zeta.max(0.0);
284 let exp = (-zeta * omega * t).exp();
285 let a = 1.0 - x0; if (zeta - 1.0).abs() < 1e-6 {
288 let b = v0 + omega * a;
290 let progress = 1.0 - (a + b * t) * exp;
291 let velocity = (a * omega - b + b * omega * t) * exp;
292 (progress, velocity)
293 } else if zeta < 1.0 {
294 let wd = omega * (1.0 - zeta * zeta).sqrt();
296 let c = (v0 + zeta * omega * a) / wd;
297 let cos_wd = (wd * t).cos();
298 let sin_wd = (wd * t).sin();
299 let env = a * cos_wd + c * sin_wd;
300 let progress = 1.0 - exp * env;
301 let velocity =
302 exp * ((zeta * omega * a - wd * c) * cos_wd + (zeta * omega * c + wd * a) * sin_wd);
303 (progress, velocity)
304 } else {
305 let wd = omega * (zeta * zeta - 1.0).sqrt();
307 let d = (v0 + zeta * omega * a) / wd;
308 let cosh_wd = (wd * t).cosh();
309 let sinh_wd = (wd * t).sinh();
310 let env = a * cosh_wd + d * sinh_wd;
311 let progress = 1.0 - exp * env;
312 let velocity =
313 exp * ((zeta * omega * a - wd * d) * cosh_wd + (zeta * omega * d - wd * a) * sinh_wd);
314 (progress, velocity)
315 }
316}
317
318fn spring_underdamped_normalized(t: f32, zeta: f32, omega: f32) -> f32 {
319 let tt = t.max(0.0);
320 let z = zeta.clamp(0.0, 0.999);
321 let w = omega.max(0.0);
322 let wd = w * (1.0 - z * z).sqrt();
323 let exp_term = (-z * w * tt).exp();
324 let cos_term = (wd * tt).cos();
325 let sin_term = (wd * tt).sin();
326 let c = z / (1.0 - z * z).sqrt();
328 let y = 1.0 - exp_term * (cos_term + c * sin_term);
329 y.clamp(0.0, 1.0)
330}
331
332#[derive(Clone, Copy, Debug)]
333pub struct AnimationSpec {
334 pub duration: Duration,
335 pub easing: Easing,
336 pub delay: Duration,
337 pub spring: Option<SpringSpec>,
339 pub repeat: Option<RepeatableSpec>,
341}
342
343impl Default for AnimationSpec {
344 fn default() -> Self {
345 Self {
346 duration: Duration::from_millis(300),
347 easing: Easing::EaseInOut,
348 delay: Duration::ZERO,
349 spring: None,
350 repeat: None,
351 }
352 }
353}
354
355impl AnimationSpec {
356 pub fn tween(duration: Duration, easing: Easing) -> Self {
357 Self {
358 duration,
359 easing,
360 delay: Duration::ZERO,
361 spring: None,
362 repeat: None,
363 }
364 }
365 pub fn spring(spring: SpringSpec) -> Self {
367 Self {
368 duration: Duration::ZERO,
369 easing: Easing::Linear,
370 delay: Duration::ZERO,
371 spring: Some(spring),
372 repeat: None,
373 }
374 }
375 pub fn spring_gentle() -> Self {
377 Self::spring(SpringSpec::gentle())
378 }
379 pub fn spring_bouncy() -> Self {
381 Self::spring(SpringSpec::bouncy())
382 }
383 pub fn spring_crit(omega: f32) -> Self {
385 Self::spring(SpringSpec::new(1.0, omega * omega))
386 }
387
388 pub fn fast() -> Self {
389 Self {
390 duration: Duration::from_millis(150),
391 easing: Easing::EaseOut,
392 delay: Duration::ZERO,
393 spring: None,
394 repeat: None,
395 }
396 }
397
398 pub fn slow() -> Self {
399 Self {
400 duration: Duration::from_millis(600),
401 easing: Easing::EaseInOut,
402 delay: Duration::ZERO,
403 spring: None,
404 repeat: None,
405 }
406 }
407
408 pub fn repeated(mut self, repeat: RepeatableSpec) -> Self {
411 self.repeat = Some(repeat);
412 self
413 }
414}
415
416#[derive(Clone, Debug)]
421pub struct KeyframesSpec<T: Clone> {
422 pub keyframes: Vec<(f32, T, Option<Easing>)>,
425}
426
427impl<T: Clone + Interpolate> KeyframesSpec<T> {
428 pub fn new(keyframes: Vec<(f32, T)>) -> Self {
429 let with_easing = keyframes.into_iter().map(|(t, v)| (t, v, None)).collect();
430 Self {
431 keyframes: with_easing,
432 }
433 }
434
435 pub fn with_easing(mut self, easing: Easing) -> Self {
437 if let Some(last) = self.keyframes.last_mut() {
438 last.2 = Some(easing);
439 }
440 self
441 }
442
443 pub fn evaluate(&self, t: f32) -> T {
444 let t = t.clamp(0.0, 1.0);
445 let kf = &self.keyframes;
446 if kf.is_empty() {
447 panic!("KeyframesSpec must have at least one keyframe");
448 }
449
450 for i in 0..kf.len() - 1 {
452 let (t0, _, _) = kf[i];
453 let (t1, ref v1, easing) = kf[i + 1];
454 if t >= t0 && t <= t1 {
455 let segment_t = if (t1 - t0).abs() < f32::EPSILON {
456 1.0
457 } else {
458 (t - t0) / (t1 - t0)
459 };
460 let eased_t = match easing {
461 Some(e) => e.interpolate(segment_t),
462 None => segment_t,
463 };
464 return kf[i].1.interpolate(v1, eased_t);
465 }
466 }
467 kf.last().unwrap().1.clone()
468 }
469}
470
471#[derive(Clone, Debug)]
478pub struct SplineKeyframes {
479 spline: MonoSpline,
480}
481
482impl SplineKeyframes {
483 pub fn new(keyframes: Vec<(f32, f32)>) -> Self {
488 assert!(
489 keyframes.len() >= 2,
490 "SplineKeyframes requires at least 2 keyframes"
491 );
492 let times: Vec<f32> = keyframes.iter().map(|(t, _)| *t).collect();
493 let values: Vec<f32> = keyframes.iter().map(|(_, v)| *v).collect();
494 Self {
495 spline: MonoSpline::new(times, values),
496 }
497 }
498
499 pub fn evaluate(&self, t: f32) -> f32 {
501 self.spline.evaluate(t.clamp(0.0, 1.0))
502 }
503}
504
505#[derive(Clone, Copy, Debug)]
510pub struct RepeatableSpec {
511 pub iterations: Option<u32>,
513 pub reverse: bool,
515 pub delay_between: Duration,
517}
518
519impl Default for RepeatableSpec {
520 fn default() -> Self {
521 Self {
522 iterations: None,
523 reverse: false,
524 delay_between: Duration::ZERO,
525 }
526 }
527}
528
529impl RepeatableSpec {
530 pub fn new(iterations: u32) -> Self {
531 Self {
532 iterations: Some(iterations),
533 reverse: false,
534 delay_between: Duration::ZERO,
535 }
536 }
537
538 pub fn infinite() -> Self {
539 Self {
540 iterations: None,
541 reverse: false,
542 delay_between: Duration::ZERO,
543 }
544 }
545
546 pub fn reverse(mut self) -> Self {
547 self.reverse = true;
548 self
549 }
550
551 pub fn delay_between(mut self, d: Duration) -> Self {
552 self.delay_between = d;
553 self
554 }
555}
556
557#[derive(Clone, Copy, Debug)]
561pub struct DecayAnimationSpec {
562 pub friction: f32,
564 pub stop_threshold: f32,
566}
567
568impl Default for DecayAnimationSpec {
569 fn default() -> Self {
570 Self {
571 friction: 0.8,
572 stop_threshold: 1.0,
573 }
574 }
575}
576
577impl DecayAnimationSpec {
578 pub fn new(friction: f32) -> Self {
579 Self {
580 friction: friction.clamp(0.01, 1.0),
581 stop_threshold: 1.0,
582 }
583 }
584}
585
586impl AnimatedValue<f32> {
587 pub fn update_decay(&mut self, friction: f32, stop_threshold: f32) -> bool {
589 let _start = match self.start_time {
590 Some(s) => s,
591 None => return false,
592 };
593
594 let now = now();
595 let dt = match self.last_update {
596 Some(last) => now.saturating_duration_since(last).as_secs_f32().min(0.05),
597 None => 0.0,
598 };
599 self.last_update = Some(now);
600
601 if dt <= 0.0 {
602 return true;
603 }
604
605 if self.velocity.abs() < stop_threshold {
606 self.velocity = 0.0;
607 self.start_time = None;
608 return false;
609 }
610
611 self.velocity *= friction.powf(dt * 60.0);
612 let delta = self.velocity * dt;
613 let new_progress = self.progress + delta;
619 self.progress = new_progress;
620 if self.progress.abs() < 0.001 && self.velocity.abs() < stop_threshold {
625 self.progress = 0.0;
626 self.velocity = 0.0;
627 self.start_time = None;
628 return false;
629 }
630
631 self.current = self.start.interpolate(&self.target, self.progress);
632 true
633 }
634}
635
636pub trait Interpolate {
637 fn interpolate(&self, other: &Self, t: f32) -> Self;
638}
639
640impl Interpolate for f32 {
641 fn interpolate(&self, other: &Self, t: f32) -> Self {
642 self + (other - self) * t
643 }
644}
645
646impl Interpolate for crate::Color {
647 fn interpolate(&self, other: &Self, t: f32) -> Self {
648 let lerp = |a: u8, b: u8| {
649 (a as f32 + (b as f32 - a as f32) * t)
650 .round()
651 .clamp(0.0, 255.0) as u8
652 };
653 crate::Color(
654 lerp(self.0, other.0),
655 lerp(self.1, other.1),
656 lerp(self.2, other.2),
657 lerp(self.3, other.3),
658 )
659 }
660}
661
662impl Interpolate for crate::Vec2 {
663 fn interpolate(&self, other: &Self, t: f32) -> Self {
664 crate::Vec2 {
665 x: self.x.interpolate(&other.x, t),
666 y: self.y.interpolate(&other.y, t),
667 }
668 }
669}
670
671impl Interpolate for crate::Size {
672 fn interpolate(&self, other: &Self, t: f32) -> Self {
673 crate::Size {
674 width: self.width.interpolate(&other.width, t),
675 height: self.height.interpolate(&other.height, t),
676 }
677 }
678}
679
680impl Interpolate for crate::Rect {
681 fn interpolate(&self, other: &Self, t: f32) -> Self {
682 crate::Rect {
683 x: self.x.interpolate(&other.x, t),
684 y: self.y.interpolate(&other.y, t),
685 w: self.w.interpolate(&other.w, t),
686 h: self.h.interpolate(&other.h, t),
687 }
688 }
689}
690
691pub trait Clock: Send + Sync + 'static {
693 fn now(&self) -> Instant;
694}
695
696pub struct SystemClock;
697impl Clock for SystemClock {
698 fn now(&self) -> Instant {
699 Instant::now()
700 }
701}
702
703static CLOCK: OnceLock<RwLock<Box<dyn Clock>>> = OnceLock::new();
704
705pub fn set_clock(clock: Box<dyn Clock>) {
707 let lock = CLOCK.get_or_init(|| RwLock::new(Box::new(SystemClock) as Box<dyn Clock>));
708 *lock.write() = clock;
709}
710pub fn ensure_system_clock() {
712 let _ = CLOCK.get_or_init(|| RwLock::new(Box::new(SystemClock) as Box<dyn Clock>));
713}
714
715#[derive(Clone)]
717pub struct TestClock {
718 pub t: Instant,
719}
720impl Clock for TestClock {
721 fn now(&self) -> Instant {
722 self.t
723 }
724}
725
726pub struct AnimatedValue<T: Interpolate + Clone> {
735 current: T,
736 target: T,
737 start: T,
738 spec: AnimationSpec,
739 keyframes: Option<KeyframesSpec<T>>,
740 iteration: u32,
741 start_time: Option<Instant>,
742 progress: f32,
744 velocity: f32,
745 spring_v0: f32,
747 last_update: Option<Instant>,
748}
749
750impl<T: Interpolate + Clone> AnimatedValue<T> {
751 pub fn new(initial: T, spec: AnimationSpec) -> Self {
752 Self {
753 current: initial.clone(),
754 target: initial.clone(),
755 start: initial,
756 spec,
757 keyframes: None,
758 iteration: 0,
759 start_time: None,
760 progress: 1.0,
761 velocity: 0.0,
762 spring_v0: 0.0,
763 last_update: None,
764 }
765 }
766
767 pub fn set_spec(&mut self, spec: AnimationSpec) {
768 self.spec = spec;
769 }
770
771 pub fn set_keyframes(&mut self, keyframes: KeyframesSpec<T>) {
774 self.keyframes = Some(keyframes);
775 self.start_time = Some(now());
776 self.last_update = None;
777 self.iteration = 0;
778 }
779
780 pub fn set_target(&mut self, target: T) {
781 self.keyframes = None;
786 self.start = self.current.clone();
787 self.target = target;
788 self.start_time = Some(now());
789 self.last_update = None;
790 self.iteration = 0;
791 if self.spec.spring.is_some() {
792 self.progress = 0.0;
794 self.spring_v0 = self.velocity;
795 }
796 }
797
798 pub fn snap_to(&mut self, value: T) {
800 self.current = value.clone();
801 self.target = value.clone();
802 self.start = value;
803 self.keyframes = None;
804 self.start_time = None;
805 self.progress = 1.0;
806 self.velocity = 0.0;
807 self.spring_v0 = 0.0;
808 self.last_update = None;
809 }
810
811 pub fn update(&mut self) -> bool {
812 let spring_spec = self.spec.spring;
813 let mut still = if let Some(spring) = spring_spec {
814 self.update_spring(&spring)
815 } else if self.keyframes.is_some() {
816 self.update_keyframes()
817 } else {
818 self.update_tween()
819 };
820
821 if !still {
822 if let Some(repeat) = &self.spec.repeat {
824 let maxed = repeat
825 .iterations
826 .is_some_and(|max| self.iteration + 1 >= max);
827 if !maxed {
828 self.iteration += 1;
829 if repeat.reverse {
830 std::mem::swap(&mut self.start, &mut self.target);
831 }
832 self.progress = 0.0;
833 self.velocity = 0.0;
834 self.start_time = Some(now());
835 self.last_update = None;
836 still = true;
837 }
838 }
839 }
840
841 still
842 }
843
844 fn update_keyframes(&mut self) -> bool {
845 let start = match self.start_time {
846 Some(s) => s,
847 None => return false,
848 };
849 let elapsed = now().saturating_duration_since(start);
850 if elapsed < self.spec.delay {
851 return true;
852 }
853 let animation_time = elapsed - self.spec.delay;
854 if animation_time >= self.spec.duration {
855 if let Some(ref kf) = self.keyframes {
856 self.current = kf.evaluate(1.0);
857 }
858 self.start_time = None;
859 return false;
860 }
861 let t = (animation_time.as_secs_f32() / self.spec.duration.as_secs_f32()).clamp(0.0, 1.0);
862 let eased_t = self.spec.easing.interpolate(t).clamp(0.0, 1.0);
863 if let Some(ref kf) = self.keyframes {
864 self.current = kf.evaluate(eased_t);
865 }
866 true
867 }
868
869 fn update_spring(&mut self, spring: &SpringSpec) -> bool {
870 let start = match self.start_time {
871 Some(s) => s,
872 None => return false,
873 };
874
875 let now = now();
876 let elapsed = now.saturating_duration_since(start);
877
878 if elapsed < self.spec.delay {
880 return true;
881 }
882
883 let t = elapsed.as_secs_f32().max(0.0);
884 let (progress, velocity) = spring_analytical(
885 spring.damping_ratio,
886 spring.stiffness,
887 t,
888 0.0,
889 self.spring_v0,
890 );
891 let progress = progress.clamp(-0.1, 2.0);
892
893 if (progress - 1.0).abs() < spring.settle_progress
895 && velocity.abs() < spring.settle_velocity
896 {
897 self.progress = 1.0;
898 self.velocity = 0.0;
899 self.spring_v0 = 0.0;
900 self.current = self.target.clone();
901 self.start_time = None;
902 self.last_update = None;
903 return false;
904 }
905
906 self.progress = progress;
907 self.velocity = velocity;
908 self.current = self.start.interpolate(&self.target, self.progress);
909 true
910 }
911
912 fn update_tween(&mut self) -> bool {
913 if let Some(start) = self.start_time {
914 let elapsed = now().saturating_duration_since(start);
915
916 if elapsed < self.spec.delay {
917 return true;
918 }
919
920 let animation_time = elapsed - self.spec.delay;
921
922 if animation_time >= self.spec.duration {
923 self.current = self.target.clone();
924 self.start_time = None;
925 return false;
926 }
927
928 let t =
929 (animation_time.as_secs_f32() / self.spec.duration.as_secs_f32()).clamp(0.0, 1.0);
930 let eased_t = self.spec.easing.interpolate(t);
931 let eased_t = eased_t.clamp(0.0, 1.0);
932
933 self.current = self.start.interpolate(&self.target, eased_t);
934 true
935 } else {
936 false
937 }
938 }
939
940 pub fn get(&self) -> &T {
941 &self.current
942 }
943
944 pub fn is_animating(&self) -> bool {
945 self.start_time.is_some()
946 }
947
948 pub fn has_keyframes(&self) -> bool {
949 self.keyframes.is_some()
950 }
951}