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}