lilt/
traits.rs

1/// An interface for interacting with time.
2pub trait AnimationTime: Copy + std::fmt::Debug + Send {
3    fn elapsed_since(self, time: Self) -> f32;
4}
5
6impl AnimationTime for web_time::Instant {
7    fn elapsed_since(self, time: Self) -> f32 {
8        (self - time).as_millis() as f32
9    }
10}
11
12impl AnimationTime for f32 {
13    fn elapsed_since(self, time: Self) -> f32 {
14        self - time
15    }
16}
17
18/// Defines a float representation for arbitrary types
19///
20/// The actual float values are pretty arbitrary - as interpolation from
21/// one float to another will look the same, however in the case of
22/// asymmetric animations the 'direction' of the animation is determined
23/// using these float representations.
24/// In general, this defines 'keyframes' & associates animated values on
25/// a continuous axis so that transitions & interruptions can be represented.
26pub trait FloatRepresentable {
27    fn float_value(&self) -> f32;
28}
29
30impl FloatRepresentable for bool {
31    fn float_value(&self) -> f32 {
32        if *self {
33            1.
34        } else {
35            0.
36        }
37    }
38}
39
40impl FloatRepresentable for f32 {
41    fn float_value(&self) -> f32 {
42        *self
43    }
44}
45
46/// A type implementing `Interpolable` can be used with `Animated<T>.animate(...)`
47pub trait Interpolable {
48    fn interpolated(&self, other: Self, ratio: f32) -> Self;
49}
50
51impl Interpolable for f32 {
52    fn interpolated(&self, other: Self, ratio: f32) -> Self {
53        self * (1.0 - ratio) + other * ratio
54    }
55}
56
57impl<T> Interpolable for Option<T>
58where
59    T: Interpolable + Copy,
60{
61    fn interpolated(&self, other: Self, ratio: f32) -> Self {
62        match (self, other) {
63            (Some(a), Some(b)) => Some(a.interpolated(b, ratio)),
64            _ => None,
65        }
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_f32_interpolation() {
75        let start = 0.0f32;
76        let end = 10.0f32;
77
78        assert_eq!(start.interpolated(end, 0.0), 0.0);
79        assert_eq!(start.interpolated(end, 0.5), 5.0);
80        assert_eq!(start.interpolated(end, 1.0), 10.0);
81        assert_eq!(start.interpolated(end, 0.25), 2.5);
82        assert_eq!(start.interpolated(end, 0.75), 7.5);
83    }
84
85    #[test]
86    fn test_option_f32_interpolation() {
87        let start = Some(0.0f32);
88        let end = Some(10.0f32);
89
90        assert_eq!(start.interpolated(end, 0.0), Some(0.0));
91        assert_eq!(start.interpolated(end, 0.5), Some(5.0));
92        assert_eq!(start.interpolated(end, 1.0), Some(10.0));
93        assert_eq!(start.interpolated(end, 0.25), Some(2.5));
94        assert_eq!(start.interpolated(end, 0.75), Some(7.5));
95    }
96
97    #[test]
98    fn test_option_f32_interpolation_with_none() {
99        let start = Some(0.0f32);
100        let end = None;
101
102        assert_eq!(start.interpolated(end, 0.0), None);
103        assert_eq!(start.interpolated(end, 0.5), None);
104        assert_eq!(start.interpolated(end, 1.0), None);
105
106        let start = None;
107        let end = Some(10.0f32);
108
109        assert_eq!(start.interpolated(end, 0.0), None);
110        assert_eq!(start.interpolated(end, 0.5), None);
111        assert_eq!(start.interpolated(end, 1.0), None);
112
113        let start: Option<f32> = None;
114        let end: Option<f32> = None;
115        assert_eq!(start.interpolated(end, 0.0), None);
116        assert_eq!(start.interpolated(end, 0.5), None);
117        assert_eq!(start.interpolated(end, 1.0), None);
118    }
119}