dioxus_motion/
keyframes.rs1use 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}