rtools/
animation.rs

1use chrono::Utc;
2
3const SEC: f32 = 1_000.0;
4
5#[derive(Default, Debug)]
6pub struct Animation {
7    start:    f32,
8    span:     f32,
9    duration: f32,
10    stamp:    i64,
11}
12
13impl Animation {
14    pub fn new(start: impl Into<f32>, end: impl Into<f32>, duration: impl Into<f32>) -> Self {
15        let start = start.into() * SEC;
16        let end = end.into() * SEC;
17        let span = end - start;
18        assert!(span != 0.0);
19        Self {
20            start,
21            span,
22            duration: duration.into() * SEC,
23            stamp: Utc::now().timestamp_millis(),
24        }
25    }
26
27    #[allow(clippy::cast_possible_truncation)]
28    pub fn finished(&self) -> bool {
29        Utc::now().timestamp_millis() >= self.stamp + self.duration as i64
30    }
31
32    #[allow(clippy::cast_possible_truncation)]
33    #[allow(clippy::cast_sign_loss)]
34    #[allow(clippy::cast_precision_loss)]
35    pub fn value(&self) -> f32 {
36        let now = Utc::now().timestamp_millis();
37        let delta = (now - self.stamp) as f32;
38        let passed = (delta / self.duration) as u64;
39        let even = passed % 2 == 0;
40        let passed = passed as f32;
41        let delta = delta - (passed * self.duration);
42        let ratio = delta / (self.duration);
43        let span = if even {
44            self.span * ratio
45        } else {
46            self.span - self.span * ratio
47        };
48        (self.start + span) / SEC
49    }
50}
51
52#[cfg(test)]
53mod test {
54    use crate::{Animation, sleep};
55
56    #[test]
57    fn test() {
58        let anim = Animation::new(0.0, 1.0, 0.5);
59
60        assert_eq!(anim.finished(), false);
61        assert_eq!(anim.value(), 0.0);
62
63        sleep(0.25);
64
65        assert_eq!(anim.finished(), false);
66        assert!(anim.value() >= 0.48 && anim.value() <= 0.52);
67
68        sleep(0.10);
69
70        assert_eq!(anim.finished(), false);
71        assert!(anim.value() >= 0.70 && anim.value() <= 0.725);
72
73        sleep(0.15);
74
75        assert_eq!(anim.finished(), true);
76        assert!(anim.value() >= 0.96 && anim.value() <= 1.04);
77
78        sleep(0.25);
79
80        assert_eq!(anim.finished(), true);
81        assert!(anim.value() >= 0.40 && anim.value() <= 0.60);
82    }
83}