1mod duration;
4mod iteration;
5mod mode;
6mod utils;
7
8pub use duration::{Delay, Duration};
9pub use iteration::IterationCount;
10pub use mode::Direction;
11
12pub use lilt::Easing;
13
14#[derive(Debug, Clone, Copy, PartialEq)]
30pub struct Timing {
31 duration: Duration,
33 delay: Delay,
35 direction: Direction,
37 easing: Easing,
39 iterations: IterationCount,
41}
42
43impl Timing {
44 #[must_use]
46 pub fn new(duration_ms: impl Into<Duration>) -> Self {
47 Self {
48 duration: duration_ms.into(),
49 ..Self::default()
50 }
51 }
52
53 #[must_use]
55 pub fn linear(duration_ms: impl Into<Duration>) -> Self {
56 Self::new(duration_ms)
57 }
58
59 #[must_use]
61 pub fn ease_in(duration_ms: impl Into<Duration>) -> Self {
62 Self::new(duration_ms).with_easing(Easing::EaseIn)
63 }
64
65 #[must_use]
67 pub fn ease_out(duration_ms: impl Into<Duration>) -> Self {
68 Self::new(duration_ms).with_easing(Easing::EaseOut)
69 }
70
71 #[must_use]
73 pub fn ease_in_out(duration_ms: impl Into<Duration>) -> Self {
74 Self::new(duration_ms).with_easing(Easing::EaseInOut)
75 }
76
77 #[must_use]
79 pub const fn duration(&self) -> Duration {
80 self.duration
81 }
82
83 #[must_use]
85 pub const fn delay(&self) -> Delay {
86 self.delay
87 }
88
89 #[must_use]
91 pub const fn direction(&self) -> Direction {
92 self.direction
93 }
94
95 #[must_use]
97 pub const fn easing(&self) -> Easing {
98 self.easing
99 }
100
101 #[must_use]
103 pub const fn iterations(&self) -> IterationCount {
104 self.iterations
105 }
106
107 #[must_use]
109 pub const fn with_duration(mut self, duration: Duration) -> Self {
110 self.duration = duration;
111 self
112 }
113
114 #[must_use]
116 pub const fn with_delay(mut self, delay: Delay) -> Self {
117 self.delay = delay;
118 self
119 }
120
121 #[must_use]
123 pub const fn with_direction(mut self, direction: Direction) -> Self {
124 self.direction = direction;
125 self
126 }
127
128 #[must_use]
130 pub const fn with_easing(mut self, easing: Easing) -> Self {
131 self.easing = easing;
132 self
133 }
134
135 #[must_use]
137 pub fn with_iterations(mut self, iterations: impl Into<IterationCount>) -> Self {
138 self.iterations = iterations.into();
139 self
140 }
141
142 #[must_use]
144 pub fn active_duration(self) -> Option<Duration> {
145 let count = self.iterations.finite_count()?;
146
147 self.duration.checked_mul(count)
148 }
149
150 #[must_use]
152 pub fn total_duration(self) -> Option<Duration> {
153 let active = self.active_duration()?;
154
155 active.checked_add_delay(self.delay)
156 }
157
158 pub(crate) fn with_rate(mut self, rate: f64) -> Self {
159 self.duration = self.duration.divided_by(rate);
160 self
161 }
162}
163
164impl Default for Timing {
165 fn default() -> Self {
166 Self {
167 duration: Duration::ZERO,
168 delay: Delay::ZERO,
169 direction: Direction::default(),
170 easing: Easing::Linear,
171 iterations: IterationCount::default(),
172 }
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::{Delay, Timing};
179 use float_cmp::assert_approx_eq;
180
181 #[test]
182 fn rate_scales_active_duration_without_changing_delay() {
183 let faster = Timing::new(200.0)
184 .with_delay(Delay::from_millis(40.0))
185 .with_iterations(3)
186 .with_rate(2.0);
187 let slower = Timing::new(200.0).with_rate(0.5);
188
189 assert_approx_eq!(f64, faster.duration().as_millis(), 100.0);
190 assert_approx_eq!(f64, faster.delay().as_millis(), 40.0);
191 assert_approx_eq!(f64, faster.total_duration().unwrap().as_millis(), 340.0);
192 assert_approx_eq!(f64, slower.duration().as_millis(), 400.0);
193 }
194
195 #[test]
196 fn invalid_rate_leaves_duration_unchanged() {
197 let timing = Timing::new(200.0);
198
199 assert_eq!(timing.with_rate(0.0).duration(), timing.duration());
200 assert_eq!(timing.with_rate(-1.0).duration(), timing.duration());
201 assert_eq!(timing.with_rate(f64::NAN).duration(), timing.duration());
202 assert_eq!(
203 timing.with_rate(f64::INFINITY).duration(),
204 timing.duration()
205 );
206 }
207}