1use parking_lot::RwLock;
2use std::sync::OnceLock;
3use web_time::{Duration, Instant};
4
5use crate::request_frame;
6
7pub(crate) fn now() -> Instant {
8 let lock = CLOCK.get_or_init(|| RwLock::new(Box::new(SystemClock) as Box<dyn Clock>));
9 lock.read().now()
10}
11
12#[derive(Clone, Copy, Debug)]
14pub struct SpringSpec {
15 pub damping_ratio: f32,
18 pub stiffness: f32,
20 pub settle_progress: f32,
23 pub settle_velocity: f32,
25}
26
27impl SpringSpec {
28 pub const fn new(damping_ratio: f32, stiffness: f32) -> Self {
29 Self {
30 damping_ratio,
31 stiffness,
32 settle_progress: 0.005,
33 settle_velocity: 0.1,
34 }
35 }
36 pub const fn gentle() -> Self {
38 Self::new(0.5, 200.0)
39 }
40 pub const fn bouncy() -> Self {
42 Self::new(0.2, 300.0)
43 }
44 pub const fn crit() -> Self {
46 Self::new(1.0, 200.0)
47 }
48 pub const fn stiff() -> Self {
50 Self::new(0.8, 600.0)
51 }
52
53 pub const fn with_settle_progress(mut self, threshold: f32) -> Self {
56 self.settle_progress = threshold;
57 self
58 }
59
60 pub const fn with_settle_velocity(mut self, threshold: f32) -> Self {
62 self.settle_velocity = threshold;
63 self
64 }
65}
66
67#[derive(Clone, Copy, Debug)]
68#[non_exhaustive]
69pub enum Easing {
70 Linear,
71 EaseIn,
72 EaseOut,
73 EaseInOut,
74 SpringCrit {
76 omega: f32,
77 },
78 SpringGentle,
80 SpringBouncy,
82 FastOutSlowIn,
85}
86
87impl Easing {
88 pub fn interpolate(&self, t: f32) -> f32 {
89 match self {
90 Easing::Linear => t,
91 Easing::EaseIn => t * t,
92 Easing::EaseOut => t * (2.0 - t),
93 Easing::EaseInOut => {
94 if t < 0.5 {
95 2.0 * t * t
96 } else {
97 -1.0 + (4.0 - 2.0 * t) * t
98 }
99 }
100 Easing::SpringCrit { omega } => {
101 let w = (*omega).max(0.0);
102 let tt = t.max(0.0);
103 1.0 - (1.0 + w * tt) * (-(w * tt)).exp()
105 }
106 Easing::SpringGentle => spring_underdamped_normalized(t, 0.5, 8.0),
107 Easing::SpringBouncy => spring_underdamped_normalized(t, 0.2, 12.0),
108 Easing::FastOutSlowIn => eval_cubic_bezier(0.4, 0.0, 0.2, 1.0, t),
109 }
110 }
111}
112
113fn eval_cubic_bezier(p1x: f32, p1y: f32, p2x: f32, p2y: f32, t: f32) -> f32 {
117 let t = t.clamp(0.0, 1.0);
118 if t <= 0.0 {
119 return 0.0;
120 }
121 if t >= 1.0 {
122 return 1.0;
123 }
124 let mut u = t;
125 for _ in 0..6 {
126 let omu = 1.0 - u;
127 let x = 3.0 * omu * omu * u * p1x + 3.0 * omu * u * u * p2x + u * u * u;
128 let dx = 3.0 * omu * omu * p1x + 6.0 * omu * u * (p2x - p1x) + 3.0 * u * u * (1.0 - p2x);
129 if dx.abs() < 1e-10 {
130 break;
131 }
132 u -= (x - t) / dx;
133 u = u.clamp(0.0, 1.0);
134 }
135 let omu = 1.0 - u;
136 3.0 * omu * omu * u * p1y + 3.0 * omu * u * u * p2y + u * u * u
137}
138
139fn hermite_interpolate(h: f32, x: f32, y1: f32, y2: f32, t1: f32, t2: f32) -> f32 {
145 let x2 = x * x;
146 let x3 = x2 * x;
147 h * t1 * (x - 2.0 * x2 + x3) + h * t2 * (x3 - x2) + y1 - (3.0 * x2 - 2.0 * x3) * (y1 - y2)
148}
149
150#[allow(dead_code)]
152fn hermite_differential(h: f32, x: f32, y1: f32, y2: f32, t1: f32, t2: f32) -> f32 {
153 let x2 = x * x;
154 h * (t1 - 2.0 * x * (2.0 * t1 + t2) + 3.0 * (t1 + t2) * x2) - 6.0 * (x - x2) * (y1 - y2)
155}
156
157#[derive(Clone, Debug)]
162pub struct MonoSpline {
163 times: Vec<f32>,
164 values: Vec<f32>,
165 tangents: Vec<f32>,
166}
167
168impl MonoSpline {
169 pub fn new(times: Vec<f32>, values: Vec<f32>) -> Self {
173 assert!(times.len() >= 2, "MonoSpline requires at least 2 keyframes");
174 assert_eq!(times.len(), values.len());
175 let n = times.len();
176 let mut tangents = vec![0.0; n];
177
178 let mut slopes = vec![0.0; n.saturating_sub(1)];
180 for i in 0..n - 1 {
181 let dt = times[i + 1] - times[i];
182 slopes[i] = (values[i + 1] - values[i]) / dt;
183 }
184
185 tangents[0] = slopes[0];
187 for i in 1..n - 1 {
188 tangents[i] = (slopes[i - 1] + slopes[i]) * 0.5;
189 }
190 tangents[n - 1] = slopes[n - 2];
191
192 for i in 0..n - 1 {
194 if slopes[i] == 0.0 {
195 tangents[i] = 0.0;
196 tangents[i + 1] = 0.0;
197 } else {
198 let a = tangents[i] / slopes[i];
199 let b = tangents[i + 1] / slopes[i];
200 let h = (a * a + b * b).sqrt();
201 if h > 9.0 {
202 let t = 3.0 / h;
203 tangents[i] = t * a * slopes[i];
204 tangents[i + 1] = t * b * slopes[i];
205 }
206 }
207 }
208
209 Self {
210 times,
211 values,
212 tangents,
213 }
214 }
215
216 pub fn evaluate(&self, t: f32) -> f32 {
219 let n = self.times.len();
220 let first = self.times[0];
221 let last = self.times[n - 1];
222
223 if t <= first {
224 return self.values[0] + (t - first) * self.tangents[0];
225 }
226 if t >= last {
227 return self.values[n - 1] + (t - last) * self.tangents[n - 1];
228 }
229
230 for i in 0..n - 1 {
231 if t >= self.times[i] && t <= self.times[i + 1] {
232 let h = self.times[i + 1] - self.times[i];
233 let x = (t - self.times[i]) / h;
234 return hermite_interpolate(
235 h,
236 x,
237 self.values[i],
238 self.values[i + 1],
239 self.tangents[i],
240 self.tangents[i + 1],
241 );
242 }
243 }
244
245 self.values[n - 1] }
247}
248
249fn spring_underdamped_normalized(t: f32, zeta: f32, omega: f32) -> f32 {
250 let tt = t.max(0.0);
251 let z = zeta.clamp(0.0, 0.999);
252 let w = omega.max(0.0);
253 let wd = w * (1.0 - z * z).sqrt();
254 let exp_term = (-z * w * tt).exp();
255 let cos_term = (wd * tt).cos();
256 let sin_term = (wd * tt).sin();
257 let c = z / (1.0 - z * z).sqrt();
259 let y = 1.0 - exp_term * (cos_term + c * sin_term);
260 y.clamp(0.0, 1.0)
261}
262
263#[derive(Clone, Copy, Debug)]
264pub struct AnimationSpec {
265 pub duration: Duration,
266 pub easing: Easing,
267 pub delay: Duration,
268 pub spring: Option<SpringSpec>,
270 pub repeat: Option<RepeatableSpec>,
272}
273
274impl Default for AnimationSpec {
275 fn default() -> Self {
276 Self {
277 duration: Duration::from_millis(300),
278 easing: Easing::EaseInOut,
279 delay: Duration::ZERO,
280 spring: None,
281 repeat: None,
282 }
283 }
284}
285
286impl AnimationSpec {
287 pub fn tween(duration: Duration, easing: Easing) -> Self {
288 Self {
289 duration,
290 easing,
291 delay: Duration::ZERO,
292 spring: None,
293 repeat: None,
294 }
295 }
296 pub fn spring(spring: SpringSpec) -> Self {
298 Self {
299 duration: Duration::ZERO,
300 easing: Easing::Linear,
301 delay: Duration::ZERO,
302 spring: Some(spring),
303 repeat: None,
304 }
305 }
306 pub fn spring_gentle() -> Self {
308 Self::spring(SpringSpec::gentle())
309 }
310 pub fn spring_bouncy() -> Self {
312 Self::spring(SpringSpec::bouncy())
313 }
314 pub fn spring_crit(omega: f32) -> Self {
316 Self::spring(SpringSpec::new(1.0, omega * omega))
317 }
318
319 pub fn fast() -> Self {
320 Self {
321 duration: Duration::from_millis(150),
322 easing: Easing::EaseOut,
323 delay: Duration::ZERO,
324 spring: None,
325 repeat: None,
326 }
327 }
328
329 pub fn slow() -> Self {
330 Self {
331 duration: Duration::from_millis(600),
332 easing: Easing::EaseInOut,
333 delay: Duration::ZERO,
334 spring: None,
335 repeat: None,
336 }
337 }
338
339 pub fn repeated(mut self, repeat: RepeatableSpec) -> Self {
342 self.repeat = Some(repeat);
343 self
344 }
345}
346
347#[derive(Clone, Debug)]
352pub struct KeyframesSpec<T: Clone> {
353 pub keyframes: Vec<(f32, T, Option<Easing>)>,
356}
357
358impl<T: Clone + Interpolate> KeyframesSpec<T> {
359 pub fn new(keyframes: Vec<(f32, T)>) -> Self {
360 let with_easing = keyframes.into_iter().map(|(t, v)| (t, v, None)).collect();
361 Self {
362 keyframes: with_easing,
363 }
364 }
365
366 pub fn with_easing(mut self, easing: Easing) -> Self {
368 if let Some(last) = self.keyframes.last_mut() {
369 last.2 = Some(easing);
370 }
371 self
372 }
373
374 pub fn evaluate(&self, t: f32) -> T {
375 let t = t.clamp(0.0, 1.0);
376 let kf = &self.keyframes;
377 if kf.is_empty() {
378 panic!("KeyframesSpec must have at least one keyframe");
379 }
380
381 for i in 0..kf.len() - 1 {
383 let (t0, _, _) = kf[i];
384 let (t1, ref v1, easing) = kf[i + 1];
385 if t >= t0 && t <= t1 {
386 let segment_t = if (t1 - t0).abs() < f32::EPSILON {
387 1.0
388 } else {
389 (t - t0) / (t1 - t0)
390 };
391 let eased_t = match easing {
392 Some(e) => e.interpolate(segment_t),
393 None => segment_t,
394 };
395 return kf[i].1.interpolate(v1, eased_t);
396 }
397 }
398 kf.last().unwrap().1.clone()
399 }
400}
401
402#[derive(Clone, Debug)]
409pub struct SplineKeyframes {
410 spline: MonoSpline,
411}
412
413impl SplineKeyframes {
414 pub fn new(keyframes: Vec<(f32, f32)>) -> Self {
419 assert!(
420 keyframes.len() >= 2,
421 "SplineKeyframes requires at least 2 keyframes"
422 );
423 let times: Vec<f32> = keyframes.iter().map(|(t, _)| *t).collect();
424 let values: Vec<f32> = keyframes.iter().map(|(_, v)| *v).collect();
425 Self {
426 spline: MonoSpline::new(times, values),
427 }
428 }
429
430 pub fn evaluate(&self, t: f32) -> f32 {
432 self.spline.evaluate(t.clamp(0.0, 1.0))
433 }
434}
435
436#[derive(Clone, Copy, Debug)]
441pub struct RepeatableSpec {
442 pub iterations: Option<u32>,
444 pub reverse: bool,
446 pub delay_between: Duration,
448}
449
450impl Default for RepeatableSpec {
451 fn default() -> Self {
452 Self {
453 iterations: None,
454 reverse: false,
455 delay_between: Duration::ZERO,
456 }
457 }
458}
459
460impl RepeatableSpec {
461 pub fn new(iterations: u32) -> Self {
462 Self {
463 iterations: Some(iterations),
464 reverse: false,
465 delay_between: Duration::ZERO,
466 }
467 }
468
469 pub fn infinite() -> Self {
470 Self {
471 iterations: None,
472 reverse: false,
473 delay_between: Duration::ZERO,
474 }
475 }
476
477 pub fn reverse(mut self) -> Self {
478 self.reverse = true;
479 self
480 }
481
482 pub fn delay_between(mut self, d: Duration) -> Self {
483 self.delay_between = d;
484 self
485 }
486}
487
488#[derive(Clone, Copy, Debug)]
492pub struct DecayAnimationSpec {
493 pub friction: f32,
495 pub stop_threshold: f32,
497}
498
499impl Default for DecayAnimationSpec {
500 fn default() -> Self {
501 Self {
502 friction: 0.8,
503 stop_threshold: 1.0,
504 }
505 }
506}
507
508impl DecayAnimationSpec {
509 pub fn new(friction: f32) -> Self {
510 Self {
511 friction: friction.clamp(0.01, 1.0),
512 stop_threshold: 1.0,
513 }
514 }
515}
516
517impl AnimatedValue<f32> {
518 pub fn update_decay(&mut self, friction: f32, stop_threshold: f32) -> bool {
520 let _start = match self.start_time {
521 Some(s) => s,
522 None => return false,
523 };
524
525 let now = now();
526 let dt = match self.last_update {
527 Some(last) => now.saturating_duration_since(last).as_secs_f32().min(0.05),
528 None => 0.0,
529 };
530 self.last_update = Some(now);
531
532 if dt <= 0.0 {
533 return true;
534 }
535
536 if self.velocity.abs() < stop_threshold {
537 self.velocity = 0.0;
538 self.start_time = None;
539 return false;
540 }
541
542 self.velocity *= friction.powf(dt * 60.0);
543 let delta = self.velocity * dt;
544 let new_progress = self.progress + delta;
550 self.progress = new_progress;
551 if self.progress.abs() < 0.001 && self.velocity.abs() < stop_threshold {
556 self.progress = 0.0;
557 self.velocity = 0.0;
558 self.start_time = None;
559 return false;
560 }
561
562 self.current = self.start.interpolate(&self.target, self.progress);
563 true
564 }
565}
566
567pub trait Interpolate {
568 fn interpolate(&self, other: &Self, t: f32) -> Self;
569}
570
571impl Interpolate for f32 {
572 fn interpolate(&self, other: &Self, t: f32) -> Self {
573 self + (other - self) * t
574 }
575}
576
577impl Interpolate for crate::Color {
578 fn interpolate(&self, other: &Self, t: f32) -> Self {
579 let lerp = |a: u8, b: u8| {
580 (a as f32 + (b as f32 - a as f32) * t)
581 .round()
582 .clamp(0.0, 255.0) as u8
583 };
584 crate::Color(
585 lerp(self.0, other.0),
586 lerp(self.1, other.1),
587 lerp(self.2, other.2),
588 lerp(self.3, other.3),
589 )
590 }
591}
592
593impl Interpolate for crate::Vec2 {
594 fn interpolate(&self, other: &Self, t: f32) -> Self {
595 crate::Vec2 {
596 x: self.x.interpolate(&other.x, t),
597 y: self.y.interpolate(&other.y, t),
598 }
599 }
600}
601
602impl Interpolate for crate::Size {
603 fn interpolate(&self, other: &Self, t: f32) -> Self {
604 crate::Size {
605 width: self.width.interpolate(&other.width, t),
606 height: self.height.interpolate(&other.height, t),
607 }
608 }
609}
610
611impl Interpolate for crate::Rect {
612 fn interpolate(&self, other: &Self, t: f32) -> Self {
613 crate::Rect {
614 x: self.x.interpolate(&other.x, t),
615 y: self.y.interpolate(&other.y, t),
616 w: self.w.interpolate(&other.w, t),
617 h: self.h.interpolate(&other.h, t),
618 }
619 }
620}
621
622pub trait Clock: Send + Sync + 'static {
624 fn now(&self) -> Instant;
625}
626
627pub struct SystemClock;
628impl Clock for SystemClock {
629 fn now(&self) -> Instant {
630 Instant::now()
631 }
632}
633
634static CLOCK: OnceLock<RwLock<Box<dyn Clock>>> = OnceLock::new();
635
636pub fn set_clock(clock: Box<dyn Clock>) {
638 let lock = CLOCK.get_or_init(|| RwLock::new(Box::new(SystemClock) as Box<dyn Clock>));
639 *lock.write() = clock;
640}
641pub fn ensure_system_clock() {
643 let _ = CLOCK.get_or_init(|| RwLock::new(Box::new(SystemClock) as Box<dyn Clock>));
644}
645
646#[derive(Clone)]
648pub struct TestClock {
649 pub t: Instant,
650}
651impl Clock for TestClock {
652 fn now(&self) -> Instant {
653 self.t
654 }
655}
656
657pub struct AnimatedValue<T: Interpolate + Clone> {
666 current: T,
667 target: T,
668 start: T,
669 spec: AnimationSpec,
670 keyframes: Option<KeyframesSpec<T>>,
671 iteration: u32,
672 start_time: Option<Instant>,
673 progress: f32,
675 velocity: f32,
676 last_update: Option<Instant>,
677}
678
679impl<T: Interpolate + Clone> AnimatedValue<T> {
680 pub fn new(initial: T, spec: AnimationSpec) -> Self {
681 Self {
682 current: initial.clone(),
683 target: initial.clone(),
684 start: initial,
685 spec,
686 keyframes: None,
687 iteration: 0,
688 start_time: None,
689 progress: 1.0,
690 velocity: 0.0,
691 last_update: None,
692 }
693 }
694
695 pub fn set_spec(&mut self, spec: AnimationSpec) {
696 self.spec = spec;
697 }
698
699 pub fn set_keyframes(&mut self, keyframes: KeyframesSpec<T>) {
702 self.keyframes = Some(keyframes);
703 self.start_time = Some(now());
704 self.last_update = None;
705 self.iteration = 0;
706 }
707
708 pub fn set_target(&mut self, target: T) {
709 if self.start_time.is_some() {
710 self.update();
711 }
712 self.keyframes = None;
713 self.start = self.current.clone();
714 self.target = target;
715 self.start_time = Some(now());
716 self.last_update = None;
717 self.iteration = 0;
718 if self.spec.spring.is_some() {
719 self.progress = 0.0;
721 }
722 }
723
724 pub fn snap_to(&mut self, value: T) {
726 self.current = value.clone();
727 self.target = value.clone();
728 self.start = value;
729 self.keyframes = None;
730 self.start_time = None;
731 self.progress = 1.0;
732 self.velocity = 0.0;
733 self.last_update = None;
734 }
735
736 pub fn update(&mut self) -> bool {
737 let spring_spec = self.spec.spring;
738 let mut still = if let Some(spring) = spring_spec {
739 self.update_spring(&spring)
740 } else if self.keyframes.is_some() {
741 self.update_keyframes()
742 } else {
743 self.update_tween()
744 };
745
746 if !still {
747 if let Some(repeat) = &self.spec.repeat {
749 let maxed = repeat
750 .iterations
751 .is_some_and(|max| self.iteration + 1 >= max);
752 if !maxed {
753 self.iteration += 1;
754 if repeat.reverse {
755 std::mem::swap(&mut self.start, &mut self.target);
756 }
757 self.progress = 0.0;
758 self.velocity = 0.0;
759 self.start_time = Some(now());
760 self.last_update = None;
761 still = true;
762 }
763 }
764 }
765
766 if still {
767 request_frame();
768 }
769 still
770 }
771
772 fn update_keyframes(&mut self) -> bool {
773 let start = match self.start_time {
774 Some(s) => s,
775 None => return false,
776 };
777 let elapsed = now().saturating_duration_since(start);
778 if elapsed < self.spec.delay {
779 return true;
780 }
781 let animation_time = elapsed - self.spec.delay;
782 if animation_time >= self.spec.duration {
783 if let Some(ref kf) = self.keyframes {
784 self.current = kf.evaluate(1.0);
785 }
786 self.start_time = None;
787 return false;
788 }
789 let t = (animation_time.as_secs_f32() / self.spec.duration.as_secs_f32()).clamp(0.0, 1.0);
790 let eased_t = self.spec.easing.interpolate(t).clamp(0.0, 1.0);
791 if let Some(ref kf) = self.keyframes {
792 self.current = kf.evaluate(eased_t);
793 }
794 true
795 }
796
797 fn update_spring(&mut self, spring: &SpringSpec) -> bool {
798 let start = match self.start_time {
799 Some(s) => s,
800 None => return false,
801 };
802
803 let now = now();
804 let dt = match self.last_update {
805 Some(last) => now.saturating_duration_since(last).as_secs_f32().min(0.05),
806 None => 0.0,
807 };
808 self.last_update = Some(now);
809
810 let elapsed = now.saturating_duration_since(start);
812 if elapsed < self.spec.delay {
813 return true;
814 }
815
816 if dt <= 0.0 {
817 return true;
818 }
819
820 let k = spring.stiffness;
822 let d = 2.0 * spring.damping_ratio * k.sqrt();
823 let displacement = self.progress - 1.0;
824
825 if displacement.abs() < spring.settle_progress
826 && self.velocity.abs() < spring.settle_velocity
827 {
828 self.progress = 1.0;
830 self.velocity = 0.0;
831 self.current = self.target.clone();
832 self.start_time = None;
833 self.last_update = None;
834 return false;
835 }
836
837 let acceleration = -k * displacement - d * self.velocity;
839 self.velocity += acceleration * dt;
840 self.progress += self.velocity * dt;
841
842 self.progress = self.progress.clamp(-0.1, 2.0);
844
845 self.current = self.start.interpolate(&self.target, self.progress);
846 true
847 }
848
849 fn update_tween(&mut self) -> bool {
850 if let Some(start) = self.start_time {
851 let elapsed = now().saturating_duration_since(start);
852
853 if elapsed < self.spec.delay {
854 return true;
855 }
856
857 let animation_time = elapsed - self.spec.delay;
858
859 if animation_time >= self.spec.duration {
860 self.current = self.target.clone();
861 self.start_time = None;
862 return false;
863 }
864
865 let t =
866 (animation_time.as_secs_f32() / self.spec.duration.as_secs_f32()).clamp(0.0, 1.0);
867 let eased_t = self.spec.easing.interpolate(t);
868 let eased_t = eased_t.clamp(0.0, 1.0);
869
870 self.current = self.start.interpolate(&self.target, eased_t);
871 true
872 } else {
873 false
874 }
875 }
876
877 pub fn get(&self) -> &T {
878 &self.current
879 }
880
881 pub fn is_animating(&self) -> bool {
882 self.start_time.is_some()
883 }
884
885 pub fn has_keyframes(&self) -> bool {
886 self.keyframes.is_some()
887 }
888}