dioxus_motion/
keyframes.rs

1use crate::Duration;
2use crate::animations::core::Animatable;
3use tracing::error;
4
5pub type EasingFn = fn(f32, f32, f32, f32) -> f32;
6
7#[derive(Debug, thiserror::Error)]
8pub enum KeyframeError {
9    #[error("Failed to compare keyframe offsets (possible NaN value)")]
10    InvalidOffset,
11}
12
13#[derive(Clone)]
14pub struct Keyframe<T: Animatable> {
15    pub value: T,
16    pub offset: f32,
17    pub easing: Option<EasingFn>,
18}
19
20#[derive(Clone)]
21pub struct KeyframeAnimation<T: Animatable> {
22    pub keyframes: Vec<Keyframe<T>>,
23    pub duration: Duration,
24}
25
26impl<T: Animatable> KeyframeAnimation<T> {
27    pub fn new(duration: Duration) -> Self {
28        Self {
29            keyframes: Vec::new(),
30            duration,
31        }
32    }
33
34    pub fn add_keyframe(
35        mut self,
36        value: T,
37        offset: f32,
38        easing: Option<EasingFn>,
39    ) -> Result<Self, KeyframeError> {
40        self.keyframes.push(Keyframe {
41            value,
42            offset: offset.clamp(0.0, 1.0),
43            easing,
44        });
45        self.keyframes.sort_by(|a, b| {
46            a.offset.partial_cmp(&b.offset).map_or_else(
47                || {
48                    error!(
49                        "Failed to compare keyframe offsets: {} vs {}",
50                        a.offset, b.offset
51                    );
52                    std::cmp::Ordering::Equal
53                },
54                |ordering| ordering,
55            )
56        });
57        if self.keyframes.iter().any(|k| k.offset.is_nan()) {
58            error!("Keyframe sorting failed: InvalidOffset (NaN offset)");
59            return Err(KeyframeError::InvalidOffset);
60        }
61        Ok(self)
62    }
63}