dioxus_motion/animations/
core.rs

1//! Core animation types and traits for Dioxus Motion
2//!
3//! This module contains the fundamental traits and types for implementing animations in Dioxus Motion.
4//! It provides support for both tweening and spring-based animations with configurable parameters.
5
6use std::sync::{Arc, Mutex};
7
8use crate::animations::{spring::Spring, tween::Tween};
9use instant::Duration;
10
11/// A simplified trait for types that can be animated
12///
13/// This trait leverages standard Rust operator traits for mathematical operations,
14/// reducing boilerplate and making implementations more intuitive.
15/// Only requires implementing interpolation and magnitude calculation.
16pub trait Animatable:
17    Copy
18    + 'static
19    + Default
20    + std::ops::Add<Output = Self>
21    + std::ops::Sub<Output = Self>
22    + std::ops::Mul<f32, Output = Self>
23{
24    /// Interpolates between self and target using t (0.0 to 1.0)
25    fn interpolate(&self, target: &Self, t: f32) -> Self;
26
27    /// Calculates the magnitude/distance from zero
28    /// Used for determining animation completion
29    fn magnitude(&self) -> f32;
30
31    /// Returns the epsilon threshold for this type
32    /// Default implementation provides a reasonable value for most use cases
33    fn epsilon() -> f32 {
34        0.01 // Single default epsilon for simplicity
35    }
36}
37
38/// Defines the type of animation to be used
39#[derive(Debug, Clone, Copy, PartialEq)]
40pub enum AnimationMode {
41    /// Tween animation with duration and easing
42    Tween(Tween),
43    /// Physics-based spring animation
44    Spring(Spring),
45}
46
47impl Default for AnimationMode {
48    fn default() -> Self {
49        Self::Tween(Tween::default())
50    }
51}
52
53/// Defines how the animation should loop
54#[derive(Debug, Clone, Copy, PartialEq)]
55pub enum LoopMode {
56    /// Play animation once
57    None,
58    /// Loop animation indefinitely
59    Infinite,
60    /// Loop animation a specific number of times
61    Times(u8),
62    /// Loop animation back and forth indefinitely
63    Alternate,
64    /// Loop animation back and forth a specific number of times
65    AlternateTimes(u8),
66}
67
68impl Default for LoopMode {
69    fn default() -> Self {
70        Self::None
71    }
72}
73
74pub type OnComplete = Arc<Mutex<dyn FnMut() + Send + 'static>>;
75/// Configuration for an animation
76#[derive(Clone, Default)]
77pub struct AnimationConfig {
78    /// The type of animation (Tween or Spring)
79    pub mode: AnimationMode,
80    /// How the animation should loop
81    pub loop_mode: Option<LoopMode>,
82    /// Delay before animation starts
83    pub delay: Duration,
84    /// Callback when animation completes
85    pub on_complete: Option<Arc<Mutex<dyn FnMut() + Send>>>,
86    /// Custom epsilon threshold for animation completion detection
87    /// If None, uses the type's default epsilon from Animatable::epsilon()
88    pub epsilon: Option<f32>,
89}
90
91impl AnimationConfig {
92    /// Creates a new animation configuration with specified mode
93    pub fn new(mode: AnimationMode) -> Self {
94        Self {
95            mode,
96            loop_mode: None,
97            delay: Duration::default(),
98            on_complete: None,
99            epsilon: None,
100        }
101    }
102
103    /// Sets the loop mode for the animation
104    pub fn with_loop(mut self, loop_mode: LoopMode) -> Self {
105        self.loop_mode = Some(loop_mode);
106        self
107    }
108
109    /// Sets a delay before the animation starts
110    pub fn with_delay(mut self, delay: Duration) -> Self {
111        self.delay = delay;
112        self
113    }
114
115    /// Sets a callback to be called when animation completes
116    pub fn with_on_complete<F>(mut self, f: F) -> Self
117    where
118        F: FnMut() + Send + 'static,
119    {
120        self.on_complete = Some(Arc::new(Mutex::new(f)));
121        self
122    }
123
124    /// Sets a custom epsilon threshold for animation completion detection
125    ///
126    /// # Arguments
127    /// * `epsilon` - The minimum meaningful difference between values for completion detection
128    ///
129    /// # Examples
130    /// ```rust
131    /// use dioxus_motion::prelude::*;
132    /// let config = AnimationConfig::new(AnimationMode::Spring(Spring::default()))
133    ///     .with_epsilon(0.01); // Custom threshold for page transitions
134    /// ```
135    pub fn with_epsilon(mut self, epsilon: f32) -> Self {
136        self.epsilon = Some(epsilon);
137        self
138    }
139
140    /// Gets the total duration of the animation
141    pub fn get_duration(&self) -> Duration {
142        match &self.mode {
143            AnimationMode::Spring(_) => {
144                // Springs don't have a fixed duration, estimate based on typical settling time
145                Duration::from_secs_f32(1.0) // You might want to adjust this based on spring parameters
146            }
147            AnimationMode::Tween(tween) => {
148                let base_duration = tween.duration;
149                match self.loop_mode {
150                    Some(LoopMode::Infinite) => Duration::from_secs(f32::INFINITY as u64),
151                    Some(LoopMode::Times(count)) => base_duration * count.into(),
152                    Some(LoopMode::Alternate) => Duration::from_secs(f32::INFINITY as u64),
153                    Some(LoopMode::AlternateTimes(count)) => base_duration * (count * 2).into(),
154                    Some(LoopMode::None) | None => base_duration,
155                }
156            }
157        }
158    }
159
160    /// Execute the completion callback if it exists
161    pub fn execute_completion(&mut self) {
162        if let Some(on_complete) = &self.on_complete {
163            if let Ok(mut callback) = on_complete.lock() {
164                callback();
165            }
166        }
167    }
168}