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)]
70pub struct CubicBezier {
71 pub p1x: f32,
72 pub p1y: f32,
73 pub p2x: f32,
74 pub p2y: f32,
75}
76
77impl CubicBezier {
78 pub const fn new(p1x: f32, p1y: f32, p2x: f32, p2y: f32) -> Self {
79 Self { p1x, p1y, p2x, p2y }
80 }
81}
82
83pub const EASING_EMPHASIZED_DECELERATE: CubicBezier = CubicBezier::new(0.05, 0.7, 0.1, 1.0);
85pub const EASING_STANDARD_DECELERATE: CubicBezier = CubicBezier::new(0.2, 0.0, 0.0, 1.0);
87
88#[derive(Clone, Copy, Debug)]
89#[non_exhaustive]
90pub enum Easing {
91 Linear,
92 EaseIn,
93 EaseOut,
94 EaseInOut,
95 SpringCrit {
97 omega: f32,
98 },
99 SpringGentle,
101 SpringBouncy,
103 FastOutSlowIn,
106 Custom(CubicBezier),
108}
109
110impl Easing {
111 pub fn interpolate(&self, t: f32) -> f32 {
112 match self {
113 Easing::Linear => t,
114 Easing::EaseIn => t * t,
115 Easing::EaseOut => t * (2.0 - t),
116 Easing::EaseInOut => {
117 if t < 0.5 {
118 2.0 * t * t
119 } else {
120 -1.0 + (4.0 - 2.0 * t) * t
121 }
122 }
123 Easing::SpringCrit { omega } => {
124 let w = (*omega).max(0.0);
125 let tt = t.max(0.0);
126 1.0 - (1.0 + w * tt) * (-(w * tt)).exp()
128 }
129 Easing::SpringGentle => spring_underdamped_normalized(t, 0.5, 8.0),
130 Easing::SpringBouncy => spring_underdamped_normalized(t, 0.2, 12.0),
131 Easing::FastOutSlowIn => eval_cubic_bezier(0.4, 0.0, 0.2, 1.0, t),
132 Easing::Custom(cb) => eval_cubic_bezier(cb.p1x, cb.p1y, cb.p2x, cb.p2y, t),
133 }
134 }
135}
136
137fn eval_cubic_bezier(p1x: f32, p1y: f32, p2x: f32, p2y: f32, t: f32) -> f32 {
141 let t = t.clamp(0.0, 1.0);
142 if t <= 0.0 {
143 return 0.0;
144 }
145 if t >= 1.0 {
146 return 1.0;
147 }
148 let mut u = t;
149 for _ in 0..6 {
150 let omu = 1.0 - u;
151 let x = 3.0 * omu * omu * u * p1x + 3.0 * omu * u * u * p2x + u * u * u;
152 let dx = 3.0 * omu * omu * p1x + 6.0 * omu * u * (p2x - p1x) + 3.0 * u * u * (1.0 - p2x);
153 if dx.abs() < 1e-10 {
154 break;
155 }
156 u -= (x - t) / dx;
157 u = u.clamp(0.0, 1.0);
158 }
159 let omu = 1.0 - u;
160 3.0 * omu * omu * u * p1y + 3.0 * omu * u * u * p2y + u * u * u
161}
162
163fn hermite_interpolate(h: f32, x: f32, y1: f32, y2: f32, t1: f32, t2: f32) -> f32 {
169 let x2 = x * x;
170 let x3 = x2 * x;
171 h * t1 * (x - 2.0 * x2 + x3) + h * t2 * (x3 - x2) + y1 - (3.0 * x2 - 2.0 * x3) * (y1 - y2)
172}
173
174#[allow(dead_code)]
176fn hermite_differential(h: f32, x: f32, y1: f32, y2: f32, t1: f32, t2: f32) -> f32 {
177 let x2 = x * x;
178 h * (t1 - 2.0 * x * (2.0 * t1 + t2) + 3.0 * (t1 + t2) * x2) - 6.0 * (x - x2) * (y1 - y2)
179}
180
181#[derive(Clone, Debug)]
186pub struct MonoSpline {
187 times: Vec<f32>,
188 values: Vec<f32>,
189 tangents: Vec<f32>,
190}
191
192impl MonoSpline {
193 pub fn new(times: Vec<f32>, values: Vec<f32>) -> Self {
197 assert!(times.len() >= 2, "MonoSpline requires at least 2 keyframes");
198 assert_eq!(times.len(), values.len());
199 let n = times.len();
200 let mut tangents = vec![0.0; n];
201
202 let mut slopes = vec![0.0; n.saturating_sub(1)];
204 for i in 0..n - 1 {
205 let dt = times[i + 1] - times[i];
206 slopes[i] = (values[i + 1] - values[i]) / dt;
207 }
208
209 tangents[0] = slopes[0];
211 for i in 1..n - 1 {
212 tangents[i] = (slopes[i - 1] + slopes[i]) * 0.5;
213 }
214 tangents[n - 1] = slopes[n - 2];
215
216 for i in 0..n - 1 {
218 if slopes[i] == 0.0 {
219 tangents[i] = 0.0;
220 tangents[i + 1] = 0.0;
221 } else {
222 let a = tangents[i] / slopes[i];
223 let b = tangents[i + 1] / slopes[i];
224 let h = (a * a + b * b).sqrt();
225 if h > 9.0 {
226 let t = 3.0 / h;
227 tangents[i] = t * a * slopes[i];
228 tangents[i + 1] = t * b * slopes[i];
229 }
230 }
231 }
232
233 Self {
234 times,
235 values,
236 tangents,
237 }
238 }
239
240 pub fn evaluate(&self, t: f32) -> f32 {
243 let n = self.times.len();
244 let first = self.times[0];
245 let last = self.times[n - 1];
246
247 if t <= first {
248 return self.values[0] + (t - first) * self.tangents[0];
249 }
250 if t >= last {
251 return self.values[n - 1] + (t - last) * self.tangents[n - 1];
252 }
253
254 for i in 0..n - 1 {
255 if t >= self.times[i] && t <= self.times[i + 1] {
256 let h = self.times[i + 1] - self.times[i];
257 let x = (t - self.times[i]) / h;
258 return hermite_interpolate(
259 h,
260 x,
261 self.values[i],
262 self.values[i + 1],
263 self.tangents[i],
264 self.tangents[i + 1],
265 );
266 }
267 }
268
269 self.values[n - 1] }
271}
272
273fn spring_underdamped_normalized(t: f32, zeta: f32, omega: f32) -> f32 {
274 let tt = t.max(0.0);
275 let z = zeta.clamp(0.0, 0.999);
276 let w = omega.max(0.0);
277 let wd = w * (1.0 - z * z).sqrt();
278 let exp_term = (-z * w * tt).exp();
279 let cos_term = (wd * tt).cos();
280 let sin_term = (wd * tt).sin();
281 let c = z / (1.0 - z * z).sqrt();
283 let y = 1.0 - exp_term * (cos_term + c * sin_term);
284 y.clamp(0.0, 1.0)
285}
286
287#[derive(Clone, Copy, Debug)]
288pub struct AnimationSpec {
289 pub duration: Duration,
290 pub easing: Easing,
291 pub delay: Duration,
292 pub spring: Option<SpringSpec>,
294 pub repeat: Option<RepeatableSpec>,
296}
297
298impl Default for AnimationSpec {
299 fn default() -> Self {
300 Self {
301 duration: Duration::from_millis(300),
302 easing: Easing::EaseInOut,
303 delay: Duration::ZERO,
304 spring: None,
305 repeat: None,
306 }
307 }
308}
309
310impl AnimationSpec {
311 pub fn tween(duration: Duration, easing: Easing) -> Self {
312 Self {
313 duration,
314 easing,
315 delay: Duration::ZERO,
316 spring: None,
317 repeat: None,
318 }
319 }
320 pub fn spring(spring: SpringSpec) -> Self {
322 Self {
323 duration: Duration::ZERO,
324 easing: Easing::Linear,
325 delay: Duration::ZERO,
326 spring: Some(spring),
327 repeat: None,
328 }
329 }
330 pub fn spring_gentle() -> Self {
332 Self::spring(SpringSpec::gentle())
333 }
334 pub fn spring_bouncy() -> Self {
336 Self::spring(SpringSpec::bouncy())
337 }
338 pub fn spring_crit(omega: f32) -> Self {
340 Self::spring(SpringSpec::new(1.0, omega * omega))
341 }
342
343 pub fn fast() -> Self {
344 Self {
345 duration: Duration::from_millis(150),
346 easing: Easing::EaseOut,
347 delay: Duration::ZERO,
348 spring: None,
349 repeat: None,
350 }
351 }
352
353 pub fn slow() -> Self {
354 Self {
355 duration: Duration::from_millis(600),
356 easing: Easing::EaseInOut,
357 delay: Duration::ZERO,
358 spring: None,
359 repeat: None,
360 }
361 }
362
363 pub fn repeated(mut self, repeat: RepeatableSpec) -> Self {
366 self.repeat = Some(repeat);
367 self
368 }
369}
370
371#[derive(Clone, Debug)]
376pub struct KeyframesSpec<T: Clone> {
377 pub keyframes: Vec<(f32, T, Option<Easing>)>,
380}
381
382impl<T: Clone + Interpolate> KeyframesSpec<T> {
383 pub fn new(keyframes: Vec<(f32, T)>) -> Self {
384 let with_easing = keyframes.into_iter().map(|(t, v)| (t, v, None)).collect();
385 Self {
386 keyframes: with_easing,
387 }
388 }
389
390 pub fn with_easing(mut self, easing: Easing) -> Self {
392 if let Some(last) = self.keyframes.last_mut() {
393 last.2 = Some(easing);
394 }
395 self
396 }
397
398 pub fn evaluate(&self, t: f32) -> T {
399 let t = t.clamp(0.0, 1.0);
400 let kf = &self.keyframes;
401 if kf.is_empty() {
402 panic!("KeyframesSpec must have at least one keyframe");
403 }
404
405 for i in 0..kf.len() - 1 {
407 let (t0, _, _) = kf[i];
408 let (t1, ref v1, easing) = kf[i + 1];
409 if t >= t0 && t <= t1 {
410 let segment_t = if (t1 - t0).abs() < f32::EPSILON {
411 1.0
412 } else {
413 (t - t0) / (t1 - t0)
414 };
415 let eased_t = match easing {
416 Some(e) => e.interpolate(segment_t),
417 None => segment_t,
418 };
419 return kf[i].1.interpolate(v1, eased_t);
420 }
421 }
422 kf.last().unwrap().1.clone()
423 }
424}
425
426#[derive(Clone, Debug)]
433pub struct SplineKeyframes {
434 spline: MonoSpline,
435}
436
437impl SplineKeyframes {
438 pub fn new(keyframes: Vec<(f32, f32)>) -> Self {
443 assert!(
444 keyframes.len() >= 2,
445 "SplineKeyframes requires at least 2 keyframes"
446 );
447 let times: Vec<f32> = keyframes.iter().map(|(t, _)| *t).collect();
448 let values: Vec<f32> = keyframes.iter().map(|(_, v)| *v).collect();
449 Self {
450 spline: MonoSpline::new(times, values),
451 }
452 }
453
454 pub fn evaluate(&self, t: f32) -> f32 {
456 self.spline.evaluate(t.clamp(0.0, 1.0))
457 }
458}
459
460#[derive(Clone, Copy, Debug)]
465pub struct RepeatableSpec {
466 pub iterations: Option<u32>,
468 pub reverse: bool,
470 pub delay_between: Duration,
472}
473
474impl Default for RepeatableSpec {
475 fn default() -> Self {
476 Self {
477 iterations: None,
478 reverse: false,
479 delay_between: Duration::ZERO,
480 }
481 }
482}
483
484impl RepeatableSpec {
485 pub fn new(iterations: u32) -> Self {
486 Self {
487 iterations: Some(iterations),
488 reverse: false,
489 delay_between: Duration::ZERO,
490 }
491 }
492
493 pub fn infinite() -> Self {
494 Self {
495 iterations: None,
496 reverse: false,
497 delay_between: Duration::ZERO,
498 }
499 }
500
501 pub fn reverse(mut self) -> Self {
502 self.reverse = true;
503 self
504 }
505
506 pub fn delay_between(mut self, d: Duration) -> Self {
507 self.delay_between = d;
508 self
509 }
510}
511
512#[derive(Clone, Copy, Debug)]
516pub struct DecayAnimationSpec {
517 pub friction: f32,
519 pub stop_threshold: f32,
521}
522
523impl Default for DecayAnimationSpec {
524 fn default() -> Self {
525 Self {
526 friction: 0.8,
527 stop_threshold: 1.0,
528 }
529 }
530}
531
532impl DecayAnimationSpec {
533 pub fn new(friction: f32) -> Self {
534 Self {
535 friction: friction.clamp(0.01, 1.0),
536 stop_threshold: 1.0,
537 }
538 }
539}
540
541impl AnimatedValue<f32> {
542 pub fn update_decay(&mut self, friction: f32, stop_threshold: f32) -> bool {
544 let _start = match self.start_time {
545 Some(s) => s,
546 None => return false,
547 };
548
549 let now = now();
550 let dt = match self.last_update {
551 Some(last) => now.saturating_duration_since(last).as_secs_f32().min(0.05),
552 None => 0.0,
553 };
554 self.last_update = Some(now);
555
556 if dt <= 0.0 {
557 return true;
558 }
559
560 if self.velocity.abs() < stop_threshold {
561 self.velocity = 0.0;
562 self.start_time = None;
563 return false;
564 }
565
566 self.velocity *= friction.powf(dt * 60.0);
567 let delta = self.velocity * dt;
568 let new_progress = self.progress + delta;
574 self.progress = new_progress;
575 if self.progress.abs() < 0.001 && self.velocity.abs() < stop_threshold {
580 self.progress = 0.0;
581 self.velocity = 0.0;
582 self.start_time = None;
583 return false;
584 }
585
586 self.current = self.start.interpolate(&self.target, self.progress);
587 true
588 }
589}
590
591pub trait Interpolate {
592 fn interpolate(&self, other: &Self, t: f32) -> Self;
593}
594
595impl Interpolate for f32 {
596 fn interpolate(&self, other: &Self, t: f32) -> Self {
597 self + (other - self) * t
598 }
599}
600
601impl Interpolate for crate::Color {
602 fn interpolate(&self, other: &Self, t: f32) -> Self {
603 let lerp = |a: u8, b: u8| {
604 (a as f32 + (b as f32 - a as f32) * t)
605 .round()
606 .clamp(0.0, 255.0) as u8
607 };
608 crate::Color(
609 lerp(self.0, other.0),
610 lerp(self.1, other.1),
611 lerp(self.2, other.2),
612 lerp(self.3, other.3),
613 )
614 }
615}
616
617impl Interpolate for crate::Vec2 {
618 fn interpolate(&self, other: &Self, t: f32) -> Self {
619 crate::Vec2 {
620 x: self.x.interpolate(&other.x, t),
621 y: self.y.interpolate(&other.y, t),
622 }
623 }
624}
625
626impl Interpolate for crate::Size {
627 fn interpolate(&self, other: &Self, t: f32) -> Self {
628 crate::Size {
629 width: self.width.interpolate(&other.width, t),
630 height: self.height.interpolate(&other.height, t),
631 }
632 }
633}
634
635impl Interpolate for crate::Rect {
636 fn interpolate(&self, other: &Self, t: f32) -> Self {
637 crate::Rect {
638 x: self.x.interpolate(&other.x, t),
639 y: self.y.interpolate(&other.y, t),
640 w: self.w.interpolate(&other.w, t),
641 h: self.h.interpolate(&other.h, t),
642 }
643 }
644}
645
646pub trait Clock: Send + Sync + 'static {
648 fn now(&self) -> Instant;
649}
650
651pub struct SystemClock;
652impl Clock for SystemClock {
653 fn now(&self) -> Instant {
654 Instant::now()
655 }
656}
657
658static CLOCK: OnceLock<RwLock<Box<dyn Clock>>> = OnceLock::new();
659
660pub fn set_clock(clock: Box<dyn Clock>) {
662 let lock = CLOCK.get_or_init(|| RwLock::new(Box::new(SystemClock) as Box<dyn Clock>));
663 *lock.write() = clock;
664}
665pub fn ensure_system_clock() {
667 let _ = CLOCK.get_or_init(|| RwLock::new(Box::new(SystemClock) as Box<dyn Clock>));
668}
669
670#[derive(Clone)]
672pub struct TestClock {
673 pub t: Instant,
674}
675impl Clock for TestClock {
676 fn now(&self) -> Instant {
677 self.t
678 }
679}
680
681pub struct AnimatedValue<T: Interpolate + Clone> {
690 current: T,
691 target: T,
692 start: T,
693 spec: AnimationSpec,
694 keyframes: Option<KeyframesSpec<T>>,
695 iteration: u32,
696 start_time: Option<Instant>,
697 progress: f32,
699 velocity: f32,
700 last_update: Option<Instant>,
701}
702
703impl<T: Interpolate + Clone> AnimatedValue<T> {
704 pub fn new(initial: T, spec: AnimationSpec) -> Self {
705 Self {
706 current: initial.clone(),
707 target: initial.clone(),
708 start: initial,
709 spec,
710 keyframes: None,
711 iteration: 0,
712 start_time: None,
713 progress: 1.0,
714 velocity: 0.0,
715 last_update: None,
716 }
717 }
718
719 pub fn set_spec(&mut self, spec: AnimationSpec) {
720 self.spec = spec;
721 }
722
723 pub fn set_keyframes(&mut self, keyframes: KeyframesSpec<T>) {
726 self.keyframes = Some(keyframes);
727 self.start_time = Some(now());
728 self.last_update = None;
729 self.iteration = 0;
730 }
731
732 pub fn set_target(&mut self, target: T) {
733 if self.start_time.is_some() {
734 self.update();
735 }
736 self.keyframes = None;
737 self.start = self.current.clone();
738 self.target = target;
739 self.start_time = Some(now());
740 self.last_update = None;
741 self.iteration = 0;
742 if self.spec.spring.is_some() {
743 self.progress = 0.0;
745 }
746 }
747
748 pub fn snap_to(&mut self, value: T) {
750 self.current = value.clone();
751 self.target = value.clone();
752 self.start = value;
753 self.keyframes = None;
754 self.start_time = None;
755 self.progress = 1.0;
756 self.velocity = 0.0;
757 self.last_update = None;
758 }
759
760 pub fn update(&mut self) -> bool {
761 let spring_spec = self.spec.spring;
762 let mut still = if let Some(spring) = spring_spec {
763 self.update_spring(&spring)
764 } else if self.keyframes.is_some() {
765 self.update_keyframes()
766 } else {
767 self.update_tween()
768 };
769
770 if !still {
771 if let Some(repeat) = &self.spec.repeat {
773 let maxed = repeat
774 .iterations
775 .is_some_and(|max| self.iteration + 1 >= max);
776 if !maxed {
777 self.iteration += 1;
778 if repeat.reverse {
779 std::mem::swap(&mut self.start, &mut self.target);
780 }
781 self.progress = 0.0;
782 self.velocity = 0.0;
783 self.start_time = Some(now());
784 self.last_update = None;
785 still = true;
786 }
787 }
788 }
789
790 if still {
791 request_frame();
792 }
793 still
794 }
795
796 fn update_keyframes(&mut self) -> bool {
797 let start = match self.start_time {
798 Some(s) => s,
799 None => return false,
800 };
801 let elapsed = now().saturating_duration_since(start);
802 if elapsed < self.spec.delay {
803 return true;
804 }
805 let animation_time = elapsed - self.spec.delay;
806 if animation_time >= self.spec.duration {
807 if let Some(ref kf) = self.keyframes {
808 self.current = kf.evaluate(1.0);
809 }
810 self.start_time = None;
811 return false;
812 }
813 let t = (animation_time.as_secs_f32() / self.spec.duration.as_secs_f32()).clamp(0.0, 1.0);
814 let eased_t = self.spec.easing.interpolate(t).clamp(0.0, 1.0);
815 if let Some(ref kf) = self.keyframes {
816 self.current = kf.evaluate(eased_t);
817 }
818 true
819 }
820
821 fn update_spring(&mut self, spring: &SpringSpec) -> bool {
822 let start = match self.start_time {
823 Some(s) => s,
824 None => return false,
825 };
826
827 let now = now();
828 let dt = match self.last_update {
829 Some(last) => now.saturating_duration_since(last).as_secs_f32().min(0.05),
830 None => 0.0,
831 };
832 self.last_update = Some(now);
833
834 let elapsed = now.saturating_duration_since(start);
836 if elapsed < self.spec.delay {
837 return true;
838 }
839
840 if dt <= 0.0 {
841 return true;
842 }
843
844 let k = spring.stiffness;
846 let d = 2.0 * spring.damping_ratio * k.sqrt();
847 let displacement = self.progress - 1.0;
848
849 if displacement.abs() < spring.settle_progress
850 && self.velocity.abs() < spring.settle_velocity
851 {
852 self.progress = 1.0;
854 self.velocity = 0.0;
855 self.current = self.target.clone();
856 self.start_time = None;
857 self.last_update = None;
858 return false;
859 }
860
861 let acceleration = -k * displacement - d * self.velocity;
863 self.velocity += acceleration * dt;
864 self.progress += self.velocity * dt;
865
866 self.progress = self.progress.clamp(-0.1, 2.0);
868
869 self.current = self.start.interpolate(&self.target, self.progress);
870 true
871 }
872
873 fn update_tween(&mut self) -> bool {
874 if let Some(start) = self.start_time {
875 let elapsed = now().saturating_duration_since(start);
876
877 if elapsed < self.spec.delay {
878 return true;
879 }
880
881 let animation_time = elapsed - self.spec.delay;
882
883 if animation_time >= self.spec.duration {
884 self.current = self.target.clone();
885 self.start_time = None;
886 return false;
887 }
888
889 let t =
890 (animation_time.as_secs_f32() / self.spec.duration.as_secs_f32()).clamp(0.0, 1.0);
891 let eased_t = self.spec.easing.interpolate(t);
892 let eased_t = eased_t.clamp(0.0, 1.0);
893
894 self.current = self.start.interpolate(&self.target, eased_t);
895 true
896 } else {
897 false
898 }
899 }
900
901 pub fn get(&self) -> &T {
902 &self.current
903 }
904
905 pub fn is_animating(&self) -> bool {
906 self.start_time.is_some()
907 }
908
909 pub fn has_keyframes(&self) -> bool {
910 self.keyframes.is_some()
911 }
912}