Skip to main content

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, Default, PartialEq)]
55pub enum LoopMode {
56    /// Play animation once
57    #[default]
58    None,
59    /// Loop animation indefinitely
60    Infinite,
61    /// Loop animation a specific number of times
62    Times(u8),
63    /// Loop animation back and forth indefinitely
64    Alternate,
65    /// Loop animation back and forth a specific number of times
66    AlternateTimes(u8),
67}
68
69pub type OnComplete = Arc<Mutex<dyn FnMut() + Send + 'static>>;
70/// Configuration for an animation
71#[derive(Clone, Default)]
72pub struct AnimationConfig {
73    /// The type of animation (Tween or Spring)
74    pub mode: AnimationMode,
75    /// How the animation should loop
76    pub loop_mode: Option<LoopMode>,
77    /// Delay before animation starts
78    pub delay: Duration,
79    /// Callback when animation completes
80    pub on_complete: Option<Arc<Mutex<dyn FnMut() + Send>>>,
81    /// Custom epsilon threshold for animation completion detection
82    /// If None, uses the type's default epsilon from Animatable::epsilon()
83    pub epsilon: Option<f32>,
84}
85
86impl AnimationConfig {
87    /// Creates a new animation configuration with specified mode
88    pub fn new(mode: AnimationMode) -> Self {
89        Self {
90            mode,
91            loop_mode: None,
92            delay: Duration::default(),
93            on_complete: None,
94            epsilon: None,
95        }
96    }
97
98    /// Sets the loop mode for the animation
99    pub fn with_loop(mut self, loop_mode: LoopMode) -> Self {
100        self.loop_mode = Some(loop_mode);
101        self
102    }
103
104    /// Sets a delay before the animation starts
105    pub fn with_delay(mut self, delay: Duration) -> Self {
106        self.delay = delay;
107        self
108    }
109
110    /// Sets a callback to be called when animation completes
111    pub fn with_on_complete<F>(mut self, f: F) -> Self
112    where
113        F: FnMut() + Send + 'static,
114    {
115        self.on_complete = Some(Arc::new(Mutex::new(f)));
116        self
117    }
118
119    /// Sets a custom epsilon threshold for animation completion detection
120    ///
121    /// # Arguments
122    /// * `epsilon` - The minimum meaningful difference between values for completion detection
123    ///
124    /// # Examples
125    /// ```rust
126    /// use dioxus_motion::prelude::*;
127    /// let config = AnimationConfig::new(AnimationMode::Spring(Spring::default()))
128    ///     .with_epsilon(0.01); // Custom threshold for page transitions
129    /// ```
130    pub fn with_epsilon(mut self, epsilon: f32) -> Self {
131        self.epsilon = Some(epsilon);
132        self
133    }
134
135    /// Gets the total duration of the animation
136    pub fn get_duration(&self) -> Duration {
137        match &self.mode {
138            AnimationMode::Spring(_) => {
139                // Springs don't have a fixed duration, estimate based on typical settling time
140                Duration::from_secs_f32(1.0) // You might want to adjust this based on spring parameters
141            }
142            AnimationMode::Tween(tween) => {
143                let base_duration = tween.duration;
144                match self.loop_mode {
145                    Some(LoopMode::Infinite) => Duration::from_secs(f32::INFINITY as u64),
146                    Some(LoopMode::Times(count)) => base_duration * count.into(),
147                    Some(LoopMode::Alternate) => Duration::from_secs(f32::INFINITY as u64),
148                    Some(LoopMode::AlternateTimes(count)) => base_duration * (count * 2).into(),
149                    Some(LoopMode::None) | None => base_duration,
150                }
151            }
152        }
153    }
154
155    /// Execute the completion callback if it exists
156    pub fn execute_completion(&mut self) {
157        if let Some(on_complete) = &self.on_complete
158            && let Ok(mut callback) = on_complete.lock()
159        {
160            callback();
161        }
162    }
163}