Skip to main content

rust_animation/
animation.rs

1use crate::layer::Layer;
2use keyframe::{ease, functions::*};
3use std::time::Instant;
4
5#[derive(Copy, Clone, Debug)]
6pub enum EasingFunction {
7  EaseIn,
8  EaseInCubic,
9  EaseInOut,
10  EaseInOutCubic,
11  EaseInOutQuad,
12  EaseInOutQuart,
13  EaseInOutQuint,
14  EaseInQuad,
15  EaseInQuart,
16  EaseInQuint,
17  EaseOut,
18  EaseOutCubic,
19  EaseOutQuad,
20  EaseOutQuart,
21  EaseOutQuint,
22  Linear,
23  Step,
24}
25
26// CoreAnimation-style timing function (alias for EasingFunction)
27pub type CAMediaTimingFunction = EasingFunction;
28
29pub struct Animation {
30  animation_time_instance: Instant,
31  translation_x_running: bool,
32  translation_x_starting_time: u128,
33  translation_x_time_duration: f32,
34  translation_x_from_value: i32,
35  translation_x_to_value: i32,
36  translation_x_ease: EasingFunction,
37
38  translation_y_running: bool,
39  translation_y_starting_time: u128,
40  translation_y_time_duration: f32,
41  translation_y_from_value: i32,
42  translation_y_to_value: i32,
43  translation_y_ease: EasingFunction,
44
45  scale_running: bool,
46  scale_starting_time: u128,
47  scale_time_duration: f32,
48  scale_from_value: f32,
49  scale_to_value: f32,
50  scale_ease: EasingFunction,
51
52  rotation_running: bool,
53  rotation_starting_time: u128,
54  rotation_time_duration: f32,
55  rotation_from_value: i32,
56  rotation_to_value: i32,
57  rotation_ease: EasingFunction,
58
59  opacity_running: bool,
60  opacity_starting_time: u128,
61  opacity_time_duration: f32,
62  opacity_from_value: f32,
63  opacity_to_value: f32,
64  opacity_ease: EasingFunction,
65
66  // CoreAnimation-style properties
67  pub duration: f32,
68  pub timing_function: Option<EasingFunction>,
69  pub repeat_count: f32,
70  pub autoreverses: bool,
71}
72
73impl Animation {
74  pub fn new() -> Animation {
75    Animation {
76      animation_time_instance: Instant::now(),
77      translation_x_running: false,
78      translation_x_starting_time: 0,
79      translation_x_time_duration: 0.0,
80      translation_x_from_value: 0,
81      translation_x_to_value: 0,
82      translation_x_ease: EasingFunction::Linear,
83
84      translation_y_running: false,
85      translation_y_starting_time: 0,
86      translation_y_time_duration: 0.0,
87      translation_y_from_value: 0,
88      translation_y_to_value: 0,
89      translation_y_ease: EasingFunction::Linear,
90
91      scale_running: false,
92      scale_starting_time: 0,
93      scale_time_duration: 0.0,
94      scale_from_value: 0.0,
95      scale_to_value: 0.0,
96      scale_ease: EasingFunction::Linear,
97
98      rotation_running: false,
99      rotation_starting_time: 0,
100      rotation_time_duration: 0.0,
101      rotation_from_value: 0,
102      rotation_to_value: 0,
103      rotation_ease: EasingFunction::Linear,
104
105      opacity_running: false,
106      opacity_starting_time: 0,
107      opacity_time_duration: 0.0,
108      opacity_from_value: 0.0,
109      opacity_to_value: 0.0,
110      opacity_ease: EasingFunction::Linear,
111
112      duration: 0.0,
113      timing_function: None,
114      repeat_count: 0.0,
115      autoreverses: false,
116    }
117  }
118
119  fn easing_function(easing: EasingFunction, from: f32, to: f32, duration: f32) -> f32 {
120    match easing {
121      EasingFunction::EaseIn => ease(EaseIn, from, to, duration),
122      EasingFunction::EaseInCubic => ease(EaseInCubic, from, to, duration),
123      EasingFunction::EaseInOut => ease(EaseInOut, from, to, duration),
124      EasingFunction::EaseInOutCubic => ease(EaseInOutCubic, from, to, duration),
125      EasingFunction::EaseInOutQuad => ease(EaseInOutQuad, from, to, duration),
126      EasingFunction::EaseInOutQuart => ease(EaseInOutQuart, from, to, duration),
127      EasingFunction::EaseInOutQuint => ease(EaseInOutQuint, from, to, duration),
128      EasingFunction::EaseInQuad => ease(EaseInQuad, from, to, duration),
129      EasingFunction::EaseInQuart => ease(EaseInQuart, from, to, duration),
130      EasingFunction::EaseInQuint => ease(EaseInQuint, from, to, duration),
131      EasingFunction::EaseOut => ease(EaseOut, from, to, duration),
132      EasingFunction::EaseOutCubic => ease(EaseOutCubic, from, to, duration),
133      EasingFunction::EaseOutQuad => ease(EaseOutQuad, from, to, duration),
134      EasingFunction::EaseOutQuart => ease(EaseOutQuart, from, to, duration),
135      EasingFunction::EaseOutQuint => ease(EaseOutQuint, from, to, duration),
136      EasingFunction::Linear => ease(Linear, from, to, duration),
137      EasingFunction::Step => ease(Step, from, to, duration),
138    }
139  }
140
141  pub fn apply_translation_x(
142    &mut self,
143    from_value: i32,
144    to_value: i32,
145    time: f32,
146    easing: EasingFunction,
147  ) {
148    self.translation_x_running = true;
149    self.translation_x_ease = easing;
150    self.translation_x_from_value = from_value;
151    self.translation_x_to_value = to_value;
152    self.translation_x_time_duration = time * 1000.0; // msec.
153  }
154
155  pub fn apply_translation_y(
156    &mut self,
157    from_value: i32,
158    to_value: i32,
159    time: f32,
160    easing: EasingFunction,
161  ) {
162    self.translation_y_running = true;
163    self.translation_y_ease = easing;
164    self.translation_y_from_value = from_value;
165    self.translation_y_to_value = to_value;
166    self.translation_y_time_duration = time * 1000.0; // msec.
167  }
168
169  pub fn apply_rotation(
170    &mut self,
171    from_value: i32,
172    to_value: i32,
173    time: f32,
174    easing: EasingFunction,
175  ) {
176    self.rotation_running = true;
177    self.rotation_ease = easing;
178    self.rotation_from_value = from_value;
179    self.rotation_to_value = to_value;
180    self.rotation_time_duration = time * 1000.0; // msec.
181  }
182
183  pub fn apply_scale(&mut self, from_value: f32, to_value: f32, time: f32, easing: EasingFunction) {
184    self.scale_running = true;
185    self.scale_ease = easing;
186    self.scale_from_value = from_value;
187    self.scale_to_value = to_value;
188    self.scale_time_duration = time * 1000.0; // msec.
189  }
190
191  pub fn apply_opacity(
192    &mut self,
193    from_value: f32,
194    to_value: f32,
195    time: f32,
196    easing: EasingFunction,
197  ) {
198    self.opacity_running = true;
199    self.opacity_ease = easing;
200    self.opacity_from_value = from_value;
201    self.opacity_to_value = to_value;
202    self.opacity_time_duration = time * 1000.0; // msec.
203  }
204
205  // CoreAnimation-style API: Create basic animation with keyPath
206  // Note: Currently key_path is for API compatibility only. In the future, this could
207  // automatically configure the animation type based on the key path (e.g., "position.x",
208  // "opacity", "transform.scale"). For now, callers should use the set_*_value methods
209  // to configure the specific animation.
210  pub fn with_key_path(_key_path: &str) -> Animation {
211    let mut animation = Animation::new();
212    // Set default duration
213    animation.duration = 1.0;
214    animation.timing_function = Some(EasingFunction::Linear);
215    animation
216  }
217
218  // CoreAnimation-style API: Set from value for position.x
219  pub fn set_from_value_position_x(&mut self, value: i32) {
220    self.translation_x_from_value = value;
221  }
222
223  // CoreAnimation-style API: Set to value for position.x
224  pub fn set_to_value_position_x(&mut self, value: i32) {
225    self.translation_x_to_value = value;
226    self.translation_x_running = true;
227    if let Some(timing) = self.timing_function {
228      self.translation_x_ease = timing;
229    }
230    self.translation_x_time_duration = self.duration * 1000.0;
231  }
232
233  // CoreAnimation-style API: Set from value for position.y
234  pub fn set_from_value_position_y(&mut self, value: i32) {
235    self.translation_y_from_value = value;
236  }
237
238  // CoreAnimation-style API: Set to value for position.y
239  pub fn set_to_value_position_y(&mut self, value: i32) {
240    self.translation_y_to_value = value;
241    self.translation_y_running = true;
242    if let Some(timing) = self.timing_function {
243      self.translation_y_ease = timing;
244    }
245    self.translation_y_time_duration = self.duration * 1000.0;
246  }
247
248  // CoreAnimation-style API: Set from value for opacity
249  pub fn set_from_value_opacity(&mut self, value: f32) {
250    self.opacity_from_value = value;
251  }
252
253  // CoreAnimation-style API: Set to value for opacity
254  pub fn set_to_value_opacity(&mut self, value: f32) {
255    self.opacity_to_value = value;
256    self.opacity_running = true;
257    if let Some(timing) = self.timing_function {
258      self.opacity_ease = timing;
259    }
260    self.opacity_time_duration = self.duration * 1000.0;
261  }
262
263  // CoreAnimation-style API: Set from value for transform.scale
264  pub fn set_from_value_scale(&mut self, value: f32) {
265    self.scale_from_value = value;
266  }
267
268  // CoreAnimation-style API: Set to value for transform.scale
269  pub fn set_to_value_scale(&mut self, value: f32) {
270    self.scale_to_value = value;
271    self.scale_running = true;
272    if let Some(timing) = self.timing_function {
273      self.scale_ease = timing;
274    }
275    self.scale_time_duration = self.duration * 1000.0;
276  }
277
278  // CoreAnimation-style API: Set from value for transform.rotation
279  pub fn set_from_value_rotation(&mut self, value: i32) {
280    self.rotation_from_value = value;
281  }
282
283  // CoreAnimation-style API: Set to value for transform.rotation
284  pub fn set_to_value_rotation(&mut self, value: i32) {
285    self.rotation_to_value = value;
286    self.rotation_running = true;
287    if let Some(timing) = self.timing_function {
288      self.rotation_ease = timing;
289    }
290    self.rotation_time_duration = self.duration * 1000.0;
291  }
292
293  pub fn run(&mut self, layer: &mut Layer) {
294    if self.translation_x_running {
295      if self.translation_x_starting_time == 0 {
296        self.translation_x_starting_time = self.animation_time_instance.elapsed().as_millis();
297      }
298      let cur_time = (self.animation_time_instance.elapsed().as_millis()
299        - self.translation_x_starting_time) as f32
300        / self.translation_x_time_duration;
301      if cur_time <= 1.0 {
302        layer.x = Animation::easing_function(
303          self.translation_x_ease,
304          self.translation_x_from_value as f32,
305          self.translation_x_to_value as f32,
306          cur_time,
307        ) as i32;
308      } else {
309        self.translation_x_running = false;
310        self.translation_x_starting_time = 0;
311        layer.x = self.translation_x_to_value;
312      }
313    }
314
315    if self.translation_y_running {
316      if self.translation_y_starting_time == 0 {
317        self.translation_y_starting_time = self.animation_time_instance.elapsed().as_millis();
318      }
319      let cur_time = (self.animation_time_instance.elapsed().as_millis()
320        - self.translation_y_starting_time) as f32
321        / self.translation_y_time_duration;
322      if cur_time <= 1.0 {
323        layer.y = Animation::easing_function(
324          self.translation_y_ease,
325          self.translation_y_from_value as f32,
326          self.translation_y_to_value as f32,
327          cur_time,
328        ) as i32;
329      } else {
330        self.translation_y_running = false;
331        self.translation_y_starting_time = 0;
332        layer.y = self.translation_y_to_value;
333      }
334    }
335
336    if self.rotation_running {
337      if self.rotation_starting_time == 0 {
338        self.rotation_starting_time = self.animation_time_instance.elapsed().as_millis();
339      }
340
341      let cur_time = (self.animation_time_instance.elapsed().as_millis()
342        - self.rotation_starting_time) as f32
343        / self.rotation_time_duration as f32;
344      if cur_time <= 1.0 {
345        layer.rotation = Animation::easing_function(
346          self.rotation_ease,
347          self.rotation_from_value as f32,
348          self.rotation_to_value as f32,
349          cur_time,
350        ) as i32;
351      } else {
352        self.rotation_running = false;
353        self.rotation_starting_time = 0;
354        layer.rotation = self.rotation_to_value;
355      }
356    }
357
358    if self.scale_running {
359      if self.scale_starting_time == 0 {
360        self.scale_starting_time = self.animation_time_instance.elapsed().as_millis();
361      }
362
363      let cur_time = (self.animation_time_instance.elapsed().as_millis() - self.scale_starting_time)
364        as f32
365        / self.scale_time_duration as f32;
366      if cur_time <= 1.0 {
367        layer.scale_x = Animation::easing_function(
368          self.scale_ease,
369          self.scale_from_value,
370          self.scale_to_value,
371          cur_time,
372        ) as f32;
373        layer.scale_y = Animation::easing_function(
374          self.scale_ease,
375          self.scale_from_value,
376          self.scale_to_value,
377          cur_time,
378        ) as f32;
379      } else {
380        self.scale_running = false;
381        self.scale_starting_time = 0;
382        layer.scale_x = self.scale_to_value;
383        layer.scale_y = self.scale_to_value;
384      }
385    }
386
387    if self.opacity_running {
388      if self.opacity_starting_time == 0 {
389        self.opacity_starting_time = self.animation_time_instance.elapsed().as_millis();
390      }
391
392      let cur_time = (self.animation_time_instance.elapsed().as_millis()
393        - self.opacity_starting_time) as f32
394        / self.opacity_time_duration as f32;
395      if cur_time <= 1.0 {
396        layer.opacity = Animation::easing_function(
397          self.opacity_ease,
398          self.opacity_from_value,
399          self.opacity_to_value,
400          cur_time,
401        );
402      } else {
403        self.opacity_running = false;
404        self.opacity_starting_time = 0;
405        layer.opacity = self.opacity_to_value;
406      }
407    }
408
409    if self.translation_x_running
410      || self.translation_y_running
411      || self.rotation_running
412      || self.scale_running
413      || self.opacity_running
414    {
415      layer.animated = true;
416    } else {
417      layer.animated = false;
418    }
419  }
420}
421
422#[cfg(test)]
423mod tests {
424  use super::*;
425
426  #[test]
427  fn test_animation_with_key_path() {
428    let animation = Animation::with_key_path("position.x");
429    assert_eq!(animation.duration, 1.0);
430    assert!(animation.timing_function.is_some());
431  }
432
433  #[test]
434  fn test_animation_coreanimation_properties() {
435    let mut animation = Animation::with_key_path("position.x");
436    animation.duration = 3.5;
437    animation.timing_function = Some(EasingFunction::EaseInOut);
438    animation.repeat_count = 2.0;
439    animation.autoreverses = true;
440
441    assert_eq!(animation.duration, 3.5);
442    assert_eq!(animation.repeat_count, 2.0);
443    assert!(animation.autoreverses);
444  }
445
446  #[test]
447  fn test_position_animation_setters() {
448    let mut animation = Animation::with_key_path("position.x");
449    animation.duration = 2.0;
450    animation.timing_function = Some(EasingFunction::Linear);
451
452    animation.set_from_value_position_x(100);
453    animation.set_to_value_position_x(400);
454
455    assert_eq!(animation.translation_x_from_value, 100);
456    assert_eq!(animation.translation_x_to_value, 400);
457    assert!(animation.translation_x_running);
458  }
459
460  #[test]
461  fn test_opacity_animation_setters() {
462    let mut animation = Animation::with_key_path("opacity");
463    animation.duration = 1.5;
464
465    animation.set_from_value_opacity(1.0);
466    animation.set_to_value_opacity(0.5);
467
468    assert_eq!(animation.opacity_from_value, 1.0);
469    assert_eq!(animation.opacity_to_value, 0.5);
470    assert!(animation.opacity_running);
471  }
472
473  #[test]
474  fn test_scale_animation_setters() {
475    let mut animation = Animation::with_key_path("transform.scale");
476    animation.duration = 2.5;
477
478    animation.set_from_value_scale(1.0);
479    animation.set_to_value_scale(2.0);
480
481    assert_eq!(animation.scale_from_value, 1.0);
482    assert_eq!(animation.scale_to_value, 2.0);
483    assert!(animation.scale_running);
484  }
485
486  #[test]
487  fn test_rotation_animation_setters() {
488    let mut animation = Animation::with_key_path("transform.rotation");
489    animation.duration = 3.0;
490
491    animation.set_from_value_rotation(0);
492    animation.set_to_value_rotation(360);
493
494    assert_eq!(animation.rotation_from_value, 0);
495    assert_eq!(animation.rotation_to_value, 360);
496    assert!(animation.rotation_running);
497  }
498
499  #[test]
500  fn test_backward_compatibility_animation() {
501    let mut animation = Animation::new();
502    animation.apply_translation_x(0, 100, 1.0, EasingFunction::Linear);
503    animation.apply_translation_y(0, 200, 1.0, EasingFunction::EaseIn);
504    animation.apply_scale(1.0, 2.0, 1.0, EasingFunction::EaseOut);
505    animation.apply_rotation(0, 180, 1.0, EasingFunction::EaseInOut);
506
507    assert!(animation.translation_x_running);
508    assert!(animation.translation_y_running);
509    assert!(animation.scale_running);
510    assert!(animation.rotation_running);
511  }
512}