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}