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    pub(crate) fn set_rate(&mut self, rate: f64) {
52        if rate.is_finite() && rate > 0.0 {
53            let scaled = self.time / rate;
54            self.time = if scaled.is_finite() { scaled } else { f64::MAX };
55        }
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::Keyframe;
62    use float_cmp::assert_approx_eq;
63    use lilt::Easing;
64
65    #[test]
66    fn invalid_time_is_replaced_with_zero() {
67        assert_approx_eq!(f64, Keyframe::new(f64::NAN, 1.0_f32).time(), 0.0);
68        assert_approx_eq!(f64, Keyframe::new(f64::INFINITY, 1.0_f32).time(), 0.0);
69    }
70
71    #[test]
72    fn accessors_return_stored_values() {
73        let frame = Keyframe::new(25.0, 3_i32).with_easing(Easing::EaseIn);
74
75        assert_approx_eq!(f64, frame.time(), 25.0);
76        assert_eq!(*frame.value(), 3);
77        assert_eq!(frame.easing(), Easing::EaseIn);
78    }
79}