Skip to main content

rust_animation/
animation.rs

1use crate::layer::RALayer;
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(&mut self, from_value: f32, to_value: f32, time: f32, easing: EasingFunction) {
192    self.opacity_running = true;
193    self.opacity_ease = easing;
194    self.opacity_from_value = from_value;
195    self.opacity_to_value = to_value;
196    self.opacity_time_duration = time * 1000.0; // msec.
197  }
198
199  // CoreAnimation-style API: Create basic animation with keyPath
200  // Note: Currently key_path is for API compatibility only. In the future, this could
201  // automatically configure the animation type based on the key path (e.g., "position.x",
202  // "opacity", "transform.scale"). For now, callers should use the set_*_value methods
203  // to configure the specific animation.
204  pub fn with_key_path(_key_path: &str) -> Animation {
205    let mut animation = Animation::new();
206    // Set default duration
207    animation.duration = 1.0;
208    animation.timing_function = Some(EasingFunction::Linear);
209    animation
210  }
211
212  // CoreAnimation-style API: Set from value for position.x
213  pub fn set_from_value_position_x(&mut self, value: i32) {
214    self.translation_x_from_value = value;
215  }
216
217  // CoreAnimation-style API: Set to value for position.x
218  pub fn set_to_value_position_x(&mut self, value: i32) {
219    self.translation_x_to_value = value;
220    self.translation_x_running = true;
221    if let Some(timing) = self.timing_function {
222      self.translation_x_ease = timing;
223    }
224    self.translation_x_time_duration = self.duration * 1000.0;
225  }
226
227  // CoreAnimation-style API: Set from value for position.y
228  pub fn set_from_value_position_y(&mut self, value: i32) {
229    self.translation_y_from_value = value;
230  }
231
232  // CoreAnimation-style API: Set to value for position.y
233  pub fn set_to_value_position_y(&mut self, value: i32) {
234    self.translation_y_to_value = value;
235    self.translation_y_running = true;
236    if let Some(timing) = self.timing_function {
237      self.translation_y_ease = timing;
238    }
239    self.translation_y_time_duration = self.duration * 1000.0;
240  }
241
242  // CoreAnimation-style API: Set from value for opacity
243  pub fn set_from_value_opacity(&mut self, value: f32) {
244    self.opacity_from_value = value;
245  }
246
247  // CoreAnimation-style API: Set to value for opacity
248  pub fn set_to_value_opacity(&mut self, value: f32) {
249    self.opacity_to_value = value;
250    self.opacity_running = true;
251    if let Some(timing) = self.timing_function {
252      self.opacity_ease = timing;
253    }
254    self.opacity_time_duration = self.duration * 1000.0;
255  }
256
257  // CoreAnimation-style API: Set from value for transform.scale
258  pub fn set_from_value_scale(&mut self, value: f32) {
259    self.scale_from_value = value;
260  }
261
262  // CoreAnimation-style API: Set to value for transform.scale
263  pub fn set_to_value_scale(&mut self, value: f32) {
264    self.scale_to_value = value;
265    self.scale_running = true;
266    if let Some(timing) = self.timing_function {
267      self.scale_ease = timing;
268    }
269    self.scale_time_duration = self.duration * 1000.0;
270  }
271
272  // CoreAnimation-style API: Set from value for transform.rotation
273  pub fn set_from_value_rotation(&mut self, value: i32) {
274    self.rotation_from_value = value;
275  }
276
277  // CoreAnimation-style API: Set to value for transform.rotation
278  pub fn set_to_value_rotation(&mut self, value: i32) {
279    self.rotation_to_value = value;
280    self.rotation_running = true;
281    if let Some(timing) = self.timing_function {
282      self.rotation_ease = timing;
283    }
284    self.rotation_time_duration = self.duration * 1000.0;
285  }
286
287  pub fn run(&mut self, layer: &mut RALayer) {
288    if self.translation_x_running {
289      if self.translation_x_starting_time == 0 {
290        self.translation_x_starting_time = self.animation_time_instance.elapsed().as_millis();
291      }
292      let cur_time = (self.animation_time_instance.elapsed().as_millis()
293        - self.translation_x_starting_time) as f32
294        / self.translation_x_time_duration;
295      if cur_time <= 1.0 {
296        layer.x = Animation::easing_function(
297          self.translation_x_ease,
298          self.translation_x_from_value as f32,
299          self.translation_x_to_value as f32,
300          cur_time,
301        ) as i32;
302      } else {
303        self.translation_x_running = false;
304        self.translation_x_starting_time = 0;
305        layer.x = self.translation_x_to_value;
306      }
307    }
308
309    if self.translation_y_running {
310      if self.translation_y_starting_time == 0 {
311        self.translation_y_starting_time = self.animation_time_instance.elapsed().as_millis();
312      }
313      let cur_time = (self.animation_time_instance.elapsed().as_millis()
314        - self.translation_y_starting_time) as f32
315        / self.translation_y_time_duration;
316      if cur_time <= 1.0 {
317        layer.y = Animation::easing_function(
318          self.translation_y_ease,
319          self.translation_y_from_value as f32,
320          self.translation_y_to_value as f32,
321          cur_time,
322        ) as i32;
323      } else {
324        self.translation_y_running = false;
325        self.translation_y_starting_time = 0;
326        layer.y = self.translation_y_to_value;
327      }
328    }
329
330    if self.rotation_running {
331      if self.rotation_starting_time == 0 {
332        self.rotation_starting_time = self.animation_time_instance.elapsed().as_millis();
333      }
334
335      let cur_time = (self.animation_time_instance.elapsed().as_millis()
336        - self.rotation_starting_time) as f32
337        / self.rotation_time_duration as f32;
338      if cur_time <= 1.0 {
339        layer.rotation = Animation::easing_function(
340          self.rotation_ease,
341          self.rotation_from_value as f32,
342          self.rotation_to_value as f32,
343          cur_time,
344        ) as i32;
345      } else {
346        self.rotation_running = false;
347        self.rotation_starting_time = 0;
348        layer.rotation = self.rotation_to_value;
349      }
350    }
351
352    if self.scale_running {
353      if self.scale_starting_time == 0 {
354        self.scale_starting_time = self.animation_time_instance.elapsed().as_millis();
355      }
356
357      let cur_time = (self.animation_time_instance.elapsed().as_millis() - self.scale_starting_time)
358        as f32
359        / self.scale_time_duration as f32;
360      if cur_time <= 1.0 {
361        layer.scale_x = Animation::easing_function(
362          self.scale_ease,
363          self.scale_from_value,
364          self.scale_to_value,
365          cur_time,
366        ) as f32;
367        layer.scale_y = Animation::easing_function(
368          self.scale_ease,
369          self.scale_from_value,
370          self.scale_to_value,
371          cur_time,
372        ) as f32;
373      } else {
374        self.scale_running = false;
375        self.scale_starting_time = 0;
376        layer.scale_x = self.scale_to_value;
377        layer.scale_y = self.scale_to_value;
378      }
379    }
380
381    if self.opacity_running {
382      if self.opacity_starting_time == 0 {
383        self.opacity_starting_time = self.animation_time_instance.elapsed().as_millis();
384      }
385
386      let cur_time = (self.animation_time_instance.elapsed().as_millis() - self.opacity_starting_time)
387        as f32
388        / self.opacity_time_duration as f32;
389      if cur_time <= 1.0 {
390        layer.opacity = Animation::easing_function(
391          self.opacity_ease,
392          self.opacity_from_value,
393          self.opacity_to_value,
394          cur_time,
395        );
396      } else {
397        self.opacity_running = false;
398        self.opacity_starting_time = 0;
399        layer.opacity = self.opacity_to_value;
400      }
401    }
402
403    if self.translation_x_running
404      || self.translation_y_running
405      || self.rotation_running
406      || self.scale_running
407      || self.opacity_running
408    {
409      layer.animated = true;
410    } else {
411      layer.animated = false;
412    }
413  }
414}
415
416#[cfg(test)]
417mod tests {
418  use super::*;
419
420  #[test]
421  fn test_animation_with_key_path() {
422    let animation = Animation::with_key_path("position.x");
423    assert_eq!(animation.duration, 1.0);
424    assert!(animation.timing_function.is_some());
425  }
426
427  #[test]
428  fn test_animation_coreanimation_properties() {
429    let mut animation = Animation::with_key_path("position.x");
430    animation.duration = 3.5;
431    animation.timing_function = Some(EasingFunction::EaseInOut);
432    animation.repeat_count = 2.0;
433    animation.autoreverses = true;
434    
435    assert_eq!(animation.duration, 3.5);
436    assert_eq!(animation.repeat_count, 2.0);
437    assert!(animation.autoreverses);
438  }
439
440  #[test]
441  fn test_position_animation_setters() {
442    let mut animation = Animation::with_key_path("position.x");
443    animation.duration = 2.0;
444    animation.timing_function = Some(EasingFunction::Linear);
445    
446    animation.set_from_value_position_x(100);
447    animation.set_to_value_position_x(400);
448    
449    assert_eq!(animation.translation_x_from_value, 100);
450    assert_eq!(animation.translation_x_to_value, 400);
451    assert!(animation.translation_x_running);
452  }
453
454  #[test]
455  fn test_opacity_animation_setters() {
456    let mut animation = Animation::with_key_path("opacity");
457    animation.duration = 1.5;
458    
459    animation.set_from_value_opacity(1.0);
460    animation.set_to_value_opacity(0.5);
461    
462    assert_eq!(animation.opacity_from_value, 1.0);
463    assert_eq!(animation.opacity_to_value, 0.5);
464    assert!(animation.opacity_running);
465  }
466
467  #[test]
468  fn test_scale_animation_setters() {
469    let mut animation = Animation::with_key_path("transform.scale");
470    animation.duration = 2.5;
471    
472    animation.set_from_value_scale(1.0);
473    animation.set_to_value_scale(2.0);
474    
475    assert_eq!(animation.scale_from_value, 1.0);
476    assert_eq!(animation.scale_to_value, 2.0);
477    assert!(animation.scale_running);
478  }
479
480  #[test]
481  fn test_rotation_animation_setters() {
482    let mut animation = Animation::with_key_path("transform.rotation");
483    animation.duration = 3.0;
484    
485    animation.set_from_value_rotation(0);
486    animation.set_to_value_rotation(360);
487    
488    assert_eq!(animation.rotation_from_value, 0);
489    assert_eq!(animation.rotation_to_value, 360);
490    assert!(animation.rotation_running);
491  }
492
493  #[test]
494  fn test_backward_compatibility_animation() {
495    let mut animation = Animation::new();
496    animation.apply_translation_x(0, 100, 1.0, EasingFunction::Linear);
497    animation.apply_translation_y(0, 200, 1.0, EasingFunction::EaseIn);
498    animation.apply_scale(1.0, 2.0, 1.0, EasingFunction::EaseOut);
499    animation.apply_rotation(0, 180, 1.0, EasingFunction::EaseInOut);
500    
501    assert!(animation.translation_x_running);
502    assert!(animation.translation_y_running);
503    assert!(animation.scale_running);
504    assert!(animation.rotation_running);
505  }
506}