Skip to main content

aura_anim_core/keyframes/
keyframe.rs

1use lilt::Easing;
2
3use crate::traits::Animatable;
4
5/// A value sample positioned at a time in a keyframe animation.
6#[derive(Debug, Clone)]
7pub struct Keyframe<T: Animatable> {
8    time: f64,
9    value: T,
10    easing: Easing,
11}
12
13impl<T: Animatable> Keyframe<T> {
14    /// Creates a keyframe at `time` milliseconds with default easing.
15    #[must_use]
16    pub fn new(time: f64, value: T) -> Self {
17        let time = if time.is_finite() { time } else { 0.0 };
18
19        Self {
20            time,
21            value,
22            easing: Easing::default(),
23        }
24    }
25
26    /// Returns the keyframe position in milliseconds.
27    #[must_use]
28    pub fn time(&self) -> f64 {
29        self.time
30    }
31
32    /// Returns the value stored by the keyframe.
33    #[must_use]
34    pub fn value(&self) -> &T {
35        &self.value
36    }
37
38    /// Returns the easing applied after this keyframe.
39    #[must_use]
40    pub fn easing(&self) -> Easing {
41        self.easing
42    }
43
44    /// Sets the easing applied after this keyframe.
45    #[must_use]
46    pub fn with_easing(mut self, easing: Easing) -> Self {
47        self.easing = easing;
48        self
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::Keyframe;
55    use float_cmp::assert_approx_eq;
56    use lilt::Easing;
57
58    #[test]
59    fn invalid_time_is_replaced_with_zero() {
60        assert_approx_eq!(f64, Keyframe::new(f64::NAN, 1.0_f32).time(), 0.0);
61        assert_approx_eq!(f64, Keyframe::new(f64::INFINITY, 1.0_f32).time(), 0.0);
62    }
63
64    #[test]
65    fn accessors_return_stored_values() {
66        let frame = Keyframe::new(25.0, 3_i32).with_easing(Easing::EaseIn);
67
68        assert_approx_eq!(f64, frame.time(), 25.0);
69        assert_eq!(*frame.value(), 3);
70        assert_eq!(frame.easing(), Easing::EaseIn);
71    }
72}