1use super::*;
5use crate::{
6 items::{AnimationDirection, PropertyAnimation},
7 lengths::LogicalLength,
8};
9#[cfg(not(feature = "std"))]
10use num_traits::Float;
11
12enum AnimationState {
13 Delaying,
15 Animating {
17 current_iteration: u64,
18 },
19 Done {
20 iteration_count: u64,
21 },
22}
23
24pub(super) struct PropertyValueAnimationData<T> {
25 from_value: T,
26 to_value: T,
27 details: PropertyAnimation,
28 start_time: crate::animations::Instant,
29 state: AnimationState,
30}
31
32impl<T: InterpolatedPropertyValue + Clone> PropertyValueAnimationData<T> {
33 pub fn new(from_value: T, to_value: T, details: PropertyAnimation) -> Self {
34 let start_time = crate::animations::current_tick();
35
36 Self { from_value, to_value, details, start_time, state: AnimationState::Delaying }
37 }
38
39 pub fn compute_interpolated_value(&mut self) -> (T, bool) {
41 let new_tick = crate::animations::current_tick();
42 let mut time_progress = new_tick.duration_since(self.start_time).as_millis() as u64;
43 let reversed = |iteration: u64| -> bool {
44 match self.details.direction {
45 AnimationDirection::Normal => false,
46 AnimationDirection::Reverse => true,
47 AnimationDirection::Alternate => iteration % 2 == 1,
48 AnimationDirection::AlternateReverse => iteration % 2 == 0,
49 }
50 };
51
52 match self.state {
53 AnimationState::Delaying => {
54 if self.details.delay <= 0 {
55 self.state = AnimationState::Animating { current_iteration: 0 };
56 return self.compute_interpolated_value();
57 }
58
59 let delay = self.details.delay as u64;
60
61 if time_progress < delay {
62 if reversed(0) {
63 (self.to_value.clone(), false)
64 } else {
65 (self.from_value.clone(), false)
66 }
67 } else {
68 self.start_time =
69 new_tick - core::time::Duration::from_millis(time_progress - delay);
70
71 self.state = AnimationState::Animating { current_iteration: 0 };
73 self.compute_interpolated_value()
74 }
75 }
76 AnimationState::Animating { mut current_iteration } => {
77 if self.details.duration <= 0 || self.details.iteration_count == 0. {
78 self.state = AnimationState::Done { iteration_count: 0 };
79 return self.compute_interpolated_value();
80 }
81
82 let duration = self.details.duration as u64;
83 if time_progress >= duration {
84 current_iteration += time_progress / duration;
86 time_progress %= duration;
87 self.start_time = new_tick - core::time::Duration::from_millis(time_progress);
88 }
89
90 if (self.details.iteration_count < 0.)
91 || (((current_iteration * duration) + time_progress) as f64)
92 < ((self.details.iteration_count as f64) * (duration as f64))
93 {
94 self.state = AnimationState::Animating { current_iteration };
95
96 let progress = {
97 let progress =
98 (time_progress as f32 / self.details.duration as f32).clamp(0., 1.);
99 if reversed(current_iteration) { 1. - progress } else { progress }
100 };
101 let t = crate::animations::easing_curve(&self.details.easing, progress);
102 let val = self.from_value.interpolate(&self.to_value, t);
103
104 (val, false)
105 } else {
106 self.state =
107 AnimationState::Done { iteration_count: current_iteration.max(1) - 1 };
108 self.compute_interpolated_value()
109 }
110 }
111 AnimationState::Done { iteration_count } => {
112 if reversed(iteration_count) {
113 (self.from_value.clone(), true)
114 } else {
115 (self.to_value.clone(), true)
116 }
117 }
118 }
119 }
120
121 fn reset(&mut self) {
122 self.state = AnimationState::Delaying;
123 self.start_time = crate::animations::current_tick();
124 }
125}
126
127#[derive(Clone, Copy, Eq, PartialEq, Debug)]
128pub(super) enum AnimatedBindingState {
129 Animating,
130 NotAnimating,
131 ShouldStart,
132}
133
134#[pin_project::pin_project]
135pub(super) struct AnimatedBindingCallable<T, A> {
136 #[pin]
137 pub(super) original_binding: PropertyHandle,
138 pub(super) state: Cell<AnimatedBindingState>,
139 pub(super) animation_data: RefCell<PropertyValueAnimationData<T>>,
140 pub(super) compute_animation_details: A,
141}
142
143pub(super) type AnimationDetail = (PropertyAnimation, Option<crate::animations::Instant>);
144
145unsafe impl<T: InterpolatedPropertyValue + Clone, A: Fn() -> AnimationDetail> BindingCallable<T>
146 for AnimatedBindingCallable<T, A>
147{
148 fn evaluate(self: Pin<&Self>, value: &mut T) -> BindingResult {
149 let original_binding = self.project_ref().original_binding;
150 original_binding.register_as_dependency_to_current_binding(
151 #[cfg(slint_debug_property)]
152 "<AnimatedBindingCallable>",
153 );
154 match self.state.get() {
155 AnimatedBindingState::Animating => {
156 let (val, finished) = self.animation_data.borrow_mut().compute_interpolated_value();
157 *value = val;
158 if finished {
159 self.state.set(AnimatedBindingState::NotAnimating)
160 } else {
161 crate::animations::CURRENT_ANIMATION_DRIVER
162 .with(|driver| driver.set_has_active_animations());
163 }
164 }
165 AnimatedBindingState::NotAnimating => {
166 unsafe { self.original_binding.update(value as *mut T) };
168 }
169 AnimatedBindingState::ShouldStart => {
170 self.state.set(AnimatedBindingState::Animating);
171 let mut animation_data = self.animation_data.borrow_mut();
172 animation_data.from_value = value.clone();
174 let (details, start_time) = (self.compute_animation_details)();
175 if let Some(start_time) = start_time {
176 animation_data.start_time = start_time;
177 }
178 animation_data.details = details;
179
180 unsafe { self.original_binding.update((&mut animation_data.to_value) as *mut T) };
182 let (val, finished) = animation_data.compute_interpolated_value();
183 *value = val;
184 if finished {
185 self.state.set(AnimatedBindingState::NotAnimating)
186 } else {
187 crate::animations::CURRENT_ANIMATION_DRIVER
188 .with(|driver| driver.set_has_active_animations());
189 }
190 }
191 };
192 BindingResult::KeepBinding
193 }
194 fn mark_dirty(self: Pin<&Self>) {
195 if self.state.get() == AnimatedBindingState::ShouldStart {
196 return;
197 }
198 let original_dirty = self.original_binding.access(|b| b.unwrap().dirty.get());
199 if original_dirty {
200 self.state.set(AnimatedBindingState::ShouldStart);
201 self.animation_data.borrow_mut().reset();
202 }
203 }
204}
205
206pub trait InterpolatedPropertyValue: PartialEq + Default + 'static {
210 #[must_use]
214 fn interpolate(&self, target_value: &Self, t: f32) -> Self;
215}
216
217impl InterpolatedPropertyValue for f32 {
218 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
219 self + t * (target_value - self)
220 }
221}
222
223impl InterpolatedPropertyValue for i32 {
224 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
225 self + (t * (target_value - self) as f32).round() as i32
226 }
227}
228
229impl InterpolatedPropertyValue for i64 {
230 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
231 self + (t * (target_value - self) as f32).round() as Self
232 }
233}
234
235impl InterpolatedPropertyValue for u8 {
236 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
237 ((*self as f32) + (t * ((*target_value as f32) - (*self as f32)))).round().clamp(0., 255.)
238 as u8
239 }
240}
241
242impl InterpolatedPropertyValue for LogicalLength {
243 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
244 LogicalLength::new(self.get().interpolate(&target_value.get(), t))
245 }
246}
247
248impl<T: Clone + InterpolatedPropertyValue + 'static> Property<T> {
249 pub fn set_animated_value(&self, value: T, animation_data: PropertyAnimation) {
256 let d = RefCell::new(properties_animations::PropertyValueAnimationData::new(
258 self.get_internal(),
259 value,
260 animation_data,
261 ));
262 unsafe {
264 self.handle.set_binding(
265 move |val: &mut T| {
266 let (value, finished) = d.borrow_mut().compute_interpolated_value();
267 *val = value;
268 if finished {
269 BindingResult::RemoveBinding
270 } else {
271 crate::animations::CURRENT_ANIMATION_DRIVER
272 .with(|driver| driver.set_has_active_animations());
273 BindingResult::KeepBinding
274 }
275 },
276 #[cfg(slint_debug_property)]
277 self.debug_name.borrow().as_str(),
278 );
279 }
280 self.handle.mark_dirty(
281 #[cfg(slint_debug_property)]
282 self.debug_name.borrow().as_str(),
283 );
284 }
285
286 pub fn set_animated_binding(
289 &self,
290 binding: impl Binding<T> + 'static,
291 compute_animation_details: impl Fn() -> (PropertyAnimation, Option<crate::animations::Instant>)
292 + 'static,
293 ) {
294 let binding_callable = properties_animations::AnimatedBindingCallable::<T, _> {
295 original_binding: PropertyHandle {
296 handle: Cell::new(
297 (alloc_binding_holder(move |val: &mut T| {
298 *val = binding.evaluate(val);
299 BindingResult::KeepBinding
300 }) as usize)
301 | 0b10,
302 ),
303 },
304 state: Cell::new(properties_animations::AnimatedBindingState::NotAnimating),
305 animation_data: RefCell::new(properties_animations::PropertyValueAnimationData::new(
306 T::default(),
307 T::default(),
308 PropertyAnimation::default(),
309 )),
310 compute_animation_details,
311 };
312
313 unsafe {
315 self.handle.set_binding(
316 binding_callable,
317 #[cfg(slint_debug_property)]
318 self.debug_name.borrow().as_str(),
319 )
320 };
321 self.handle.mark_dirty(
322 #[cfg(slint_debug_property)]
323 self.debug_name.borrow().as_str(),
324 );
325 }
326}
327
328#[cfg(test)]
329mod animation_tests {
330 use super::*;
331
332 #[derive(Default)]
333 struct Component {
334 width: Property<i32>,
335 width_times_two: Property<i32>,
336 feed_property: Property<i32>, }
338
339 impl Component {
340 fn new_test_component() -> Rc<Self> {
341 let compo = Rc::new(Component::default());
342 let w = Rc::downgrade(&compo);
343 compo.width_times_two.set_binding(move || {
344 let compo = w.upgrade().unwrap();
345 get_prop_value(&compo.width) * 2
346 });
347
348 compo
349 }
350 }
351
352 const DURATION: std::time::Duration = std::time::Duration::from_millis(10000);
353 const DELAY: std::time::Duration = std::time::Duration::from_millis(800);
354
355 fn get_prop_value<T: Clone>(prop: &Property<T>) -> T {
357 unsafe { Pin::new_unchecked(prop).get() }
358 }
359
360 #[test]
361 fn properties_test_animation_negative_delay_triggered_by_set() {
362 let compo = Component::new_test_component();
363
364 let animation_details = PropertyAnimation {
365 delay: -25,
366 duration: DURATION.as_millis() as _,
367 iteration_count: 1.,
368 ..PropertyAnimation::default()
369 };
370
371 compo.width.set(100);
372 assert_eq!(get_prop_value(&compo.width), 100);
373 assert_eq!(get_prop_value(&compo.width_times_two), 200);
374
375 let start_time = crate::animations::current_tick();
376
377 compo.width.set_animated_value(200, animation_details);
378 assert_eq!(get_prop_value(&compo.width), 100);
379 assert_eq!(get_prop_value(&compo.width_times_two), 200);
380
381 crate::animations::CURRENT_ANIMATION_DRIVER
382 .with(|driver| driver.update_animations(start_time + DURATION / 2));
383 assert_eq!(get_prop_value(&compo.width), 150);
384 assert_eq!(get_prop_value(&compo.width_times_two), 300);
385
386 crate::animations::CURRENT_ANIMATION_DRIVER
387 .with(|driver| driver.update_animations(start_time + DURATION));
388 assert_eq!(get_prop_value(&compo.width), 200);
389 assert_eq!(get_prop_value(&compo.width_times_two), 400);
390
391 crate::animations::CURRENT_ANIMATION_DRIVER
393 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
394 assert_eq!(get_prop_value(&compo.width), 200);
395 assert_eq!(get_prop_value(&compo.width_times_two), 400);
396
397 compo.width.handle.access(|binding| assert!(binding.is_none()));
399 }
400
401 #[test]
402 fn properties_test_animation_triggered_by_set() {
403 let compo = Component::new_test_component();
404
405 let animation_details = PropertyAnimation {
406 duration: DURATION.as_millis() as _,
407 iteration_count: 1.,
408 ..PropertyAnimation::default()
409 };
410
411 compo.width.set(100);
412 assert_eq!(get_prop_value(&compo.width), 100);
413 assert_eq!(get_prop_value(&compo.width_times_two), 200);
414
415 let start_time = crate::animations::current_tick();
416
417 compo.width.set_animated_value(200, animation_details);
418 assert_eq!(get_prop_value(&compo.width), 100);
419 assert_eq!(get_prop_value(&compo.width_times_two), 200);
420
421 crate::animations::CURRENT_ANIMATION_DRIVER
422 .with(|driver| driver.update_animations(start_time + DURATION / 2));
423 assert_eq!(get_prop_value(&compo.width), 150);
424 assert_eq!(get_prop_value(&compo.width_times_two), 300);
425
426 crate::animations::CURRENT_ANIMATION_DRIVER
427 .with(|driver| driver.update_animations(start_time + DURATION));
428 assert_eq!(get_prop_value(&compo.width), 200);
429 assert_eq!(get_prop_value(&compo.width_times_two), 400);
430
431 crate::animations::CURRENT_ANIMATION_DRIVER
433 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
434 assert_eq!(get_prop_value(&compo.width), 200);
435 assert_eq!(get_prop_value(&compo.width_times_two), 400);
436
437 compo.width.handle.access(|binding| assert!(binding.is_none()));
439 }
440
441 #[test]
442 fn properties_test_delayed_animation_triggered_by_set() {
443 let compo = Component::new_test_component();
444
445 let animation_details = PropertyAnimation {
446 delay: DELAY.as_millis() as _,
447 iteration_count: 1.,
448 duration: DURATION.as_millis() as _,
449 ..PropertyAnimation::default()
450 };
451
452 compo.width.set(100);
453 assert_eq!(get_prop_value(&compo.width), 100);
454 assert_eq!(get_prop_value(&compo.width_times_two), 200);
455
456 let start_time = crate::animations::current_tick();
457
458 compo.width.set_animated_value(200, animation_details);
459 assert_eq!(get_prop_value(&compo.width), 100);
460 assert_eq!(get_prop_value(&compo.width_times_two), 200);
461
462 crate::animations::CURRENT_ANIMATION_DRIVER
464 .with(|driver| driver.update_animations(start_time + DELAY / 2));
465 assert_eq!(get_prop_value(&compo.width), 100);
466 assert_eq!(get_prop_value(&compo.width_times_two), 200);
467
468 crate::animations::CURRENT_ANIMATION_DRIVER
470 .with(|driver| driver.update_animations(start_time + DELAY));
471 assert_eq!(get_prop_value(&compo.width), 100);
472 assert_eq!(get_prop_value(&compo.width_times_two), 200);
473
474 crate::animations::CURRENT_ANIMATION_DRIVER
475 .with(|driver| driver.update_animations(start_time + DELAY + DURATION / 2));
476 assert_eq!(get_prop_value(&compo.width), 150);
477 assert_eq!(get_prop_value(&compo.width_times_two), 300);
478
479 crate::animations::CURRENT_ANIMATION_DRIVER
480 .with(|driver| driver.update_animations(start_time + DELAY + DURATION));
481 assert_eq!(get_prop_value(&compo.width), 200);
482 assert_eq!(get_prop_value(&compo.width_times_two), 400);
483
484 crate::animations::CURRENT_ANIMATION_DRIVER
486 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
487 assert_eq!(get_prop_value(&compo.width), 200);
488 assert_eq!(get_prop_value(&compo.width_times_two), 400);
489
490 compo.width.handle.access(|binding| assert!(binding.is_none()));
492 }
493
494 #[test]
495 fn properties_test_delayed_animation_fractual_iteration_triggered_by_set() {
496 let compo = Component::new_test_component();
497
498 let animation_details = PropertyAnimation {
499 delay: DELAY.as_millis() as _,
500 iteration_count: 1.5,
501 duration: DURATION.as_millis() as _,
502 ..PropertyAnimation::default()
503 };
504
505 compo.width.set(100);
506 assert_eq!(get_prop_value(&compo.width), 100);
507 assert_eq!(get_prop_value(&compo.width_times_two), 200);
508
509 let start_time = crate::animations::current_tick();
510
511 compo.width.set_animated_value(200, animation_details);
512 assert_eq!(get_prop_value(&compo.width), 100);
513 assert_eq!(get_prop_value(&compo.width_times_two), 200);
514
515 crate::animations::CURRENT_ANIMATION_DRIVER
517 .with(|driver| driver.update_animations(start_time + DELAY / 2));
518 assert_eq!(get_prop_value(&compo.width), 100);
519 assert_eq!(get_prop_value(&compo.width_times_two), 200);
520
521 crate::animations::CURRENT_ANIMATION_DRIVER
523 .with(|driver| driver.update_animations(start_time + DELAY));
524 assert_eq!(get_prop_value(&compo.width), 100);
525 assert_eq!(get_prop_value(&compo.width_times_two), 200);
526
527 crate::animations::CURRENT_ANIMATION_DRIVER
528 .with(|driver| driver.update_animations(start_time + DELAY + DURATION / 2));
529 assert_eq!(get_prop_value(&compo.width), 150);
530 assert_eq!(get_prop_value(&compo.width_times_two), 300);
531
532 crate::animations::CURRENT_ANIMATION_DRIVER
533 .with(|driver| driver.update_animations(start_time + DELAY + DURATION));
534 assert_eq!(get_prop_value(&compo.width), 100);
535 assert_eq!(get_prop_value(&compo.width_times_two), 200);
536
537 crate::animations::CURRENT_ANIMATION_DRIVER
539 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 4));
540 assert_eq!(get_prop_value(&compo.width), 125);
541 assert_eq!(get_prop_value(&compo.width_times_two), 250);
542
543 crate::animations::CURRENT_ANIMATION_DRIVER
545 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
546 assert_eq!(get_prop_value(&compo.width), 200);
547 assert_eq!(get_prop_value(&compo.width_times_two), 400);
548
549 compo.width.handle.access(|binding| assert!(binding.is_none()));
551 }
552 #[test]
553 fn properties_test_delayed_animation_null_duration_triggered_by_set() {
554 let compo = Component::new_test_component();
555
556 let animation_details = PropertyAnimation {
557 delay: DELAY.as_millis() as _,
558 iteration_count: 1.0,
559 duration: 0,
560 ..PropertyAnimation::default()
561 };
562
563 compo.width.set(100);
564 assert_eq!(get_prop_value(&compo.width), 100);
565 assert_eq!(get_prop_value(&compo.width_times_two), 200);
566
567 let start_time = crate::animations::current_tick();
568
569 compo.width.set_animated_value(200, animation_details);
570 assert_eq!(get_prop_value(&compo.width), 100);
571 assert_eq!(get_prop_value(&compo.width_times_two), 200);
572
573 crate::animations::CURRENT_ANIMATION_DRIVER
575 .with(|driver| driver.update_animations(start_time + DELAY / 2));
576 assert_eq!(get_prop_value(&compo.width), 100);
577 assert_eq!(get_prop_value(&compo.width_times_two), 200);
578
579 crate::animations::CURRENT_ANIMATION_DRIVER
581 .with(|driver| driver.update_animations(start_time + DELAY));
582 assert_eq!(get_prop_value(&compo.width), 200);
583 assert_eq!(get_prop_value(&compo.width_times_two), 400);
584
585 crate::animations::CURRENT_ANIMATION_DRIVER
587 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
588 assert_eq!(get_prop_value(&compo.width), 200);
589 assert_eq!(get_prop_value(&compo.width_times_two), 400);
590
591 compo.width.handle.access(|binding| assert!(binding.is_none()));
593 }
594
595 #[test]
596 fn properties_test_delayed_animation_negative_duration_triggered_by_set() {
597 let compo = Component::new_test_component();
598
599 let animation_details = PropertyAnimation {
600 delay: DELAY.as_millis() as _,
601 iteration_count: 1.0,
602 duration: -25,
603 ..PropertyAnimation::default()
604 };
605
606 compo.width.set(100);
607 assert_eq!(get_prop_value(&compo.width), 100);
608 assert_eq!(get_prop_value(&compo.width_times_two), 200);
609
610 let start_time = crate::animations::current_tick();
611
612 compo.width.set_animated_value(200, animation_details);
613 assert_eq!(get_prop_value(&compo.width), 100);
614 assert_eq!(get_prop_value(&compo.width_times_two), 200);
615
616 crate::animations::CURRENT_ANIMATION_DRIVER
618 .with(|driver| driver.update_animations(start_time + DELAY / 2));
619 assert_eq!(get_prop_value(&compo.width), 100);
620 assert_eq!(get_prop_value(&compo.width_times_two), 200);
621
622 crate::animations::CURRENT_ANIMATION_DRIVER
624 .with(|driver| driver.update_animations(start_time + DELAY));
625 assert_eq!(get_prop_value(&compo.width), 200);
626 assert_eq!(get_prop_value(&compo.width_times_two), 400);
627
628 crate::animations::CURRENT_ANIMATION_DRIVER
630 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
631 assert_eq!(get_prop_value(&compo.width), 200);
632 assert_eq!(get_prop_value(&compo.width_times_two), 400);
633
634 compo.width.handle.access(|binding| assert!(binding.is_none()));
636 }
637
638 #[test]
639 fn properties_test_delayed_animation_no_iteration_triggered_by_set() {
640 let compo = Component::new_test_component();
641
642 let animation_details = PropertyAnimation {
643 delay: DELAY.as_millis() as _,
644 iteration_count: 0.0,
645 duration: DURATION.as_millis() as _,
646 ..PropertyAnimation::default()
647 };
648
649 compo.width.set(100);
650 assert_eq!(get_prop_value(&compo.width), 100);
651 assert_eq!(get_prop_value(&compo.width_times_two), 200);
652
653 let start_time = crate::animations::current_tick();
654
655 compo.width.set_animated_value(200, animation_details);
656 assert_eq!(get_prop_value(&compo.width), 100);
657 assert_eq!(get_prop_value(&compo.width_times_two), 200);
658
659 crate::animations::CURRENT_ANIMATION_DRIVER
661 .with(|driver| driver.update_animations(start_time + DELAY / 2));
662 assert_eq!(get_prop_value(&compo.width), 100);
663 assert_eq!(get_prop_value(&compo.width_times_two), 200);
664
665 crate::animations::CURRENT_ANIMATION_DRIVER
667 .with(|driver| driver.update_animations(start_time + DELAY));
668 assert_eq!(get_prop_value(&compo.width), 200);
669 assert_eq!(get_prop_value(&compo.width_times_two), 400);
670
671 crate::animations::CURRENT_ANIMATION_DRIVER
673 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
674 assert_eq!(get_prop_value(&compo.width), 200);
675 assert_eq!(get_prop_value(&compo.width_times_two), 400);
676
677 compo.width.handle.access(|binding| assert!(binding.is_none()));
679 }
680
681 #[test]
682 fn properties_test_delayed_animation_negative_iteration_triggered_by_set() {
683 let compo = Component::new_test_component();
684
685 let animation_details = PropertyAnimation {
686 delay: DELAY.as_millis() as _,
687 iteration_count: -42., duration: DURATION.as_millis() as _,
689 ..PropertyAnimation::default()
690 };
691
692 compo.width.set(100);
693 assert_eq!(get_prop_value(&compo.width), 100);
694 assert_eq!(get_prop_value(&compo.width_times_two), 200);
695
696 let start_time = crate::animations::current_tick();
697
698 compo.width.set_animated_value(200, animation_details);
699 assert_eq!(get_prop_value(&compo.width), 100);
700 assert_eq!(get_prop_value(&compo.width_times_two), 200);
701
702 crate::animations::CURRENT_ANIMATION_DRIVER
704 .with(|driver| driver.update_animations(start_time + DELAY / 2));
705 assert_eq!(get_prop_value(&compo.width), 100);
706 assert_eq!(get_prop_value(&compo.width_times_two), 200);
707
708 crate::animations::CURRENT_ANIMATION_DRIVER
710 .with(|driver| driver.update_animations(start_time + DELAY));
711 assert_eq!(get_prop_value(&compo.width), 100);
712 assert_eq!(get_prop_value(&compo.width_times_two), 200);
713
714 crate::animations::CURRENT_ANIMATION_DRIVER
715 .with(|driver| driver.update_animations(start_time + DELAY + DURATION / 2));
716 assert_eq!(get_prop_value(&compo.width), 150);
717 assert_eq!(get_prop_value(&compo.width_times_two), 300);
718
719 crate::animations::CURRENT_ANIMATION_DRIVER
720 .with(|driver| driver.update_animations(start_time + DELAY + DURATION));
721 assert_eq!(get_prop_value(&compo.width), 100);
722 assert_eq!(get_prop_value(&compo.width_times_two), 200);
723
724 crate::animations::CURRENT_ANIMATION_DRIVER
726 .with(|driver| driver.update_animations(start_time + DELAY + 500 * DURATION));
727 assert_eq!(get_prop_value(&compo.width), 100);
728 assert_eq!(get_prop_value(&compo.width_times_two), 200);
729
730 crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| {
731 driver.update_animations(start_time + DELAY + 50000 * DURATION + DURATION / 2)
732 });
733 assert_eq!(get_prop_value(&compo.width), 150);
734 assert_eq!(get_prop_value(&compo.width_times_two), 300);
735
736 compo.width.handle.access(|binding| assert!(binding.is_some()));
738 }
739
740 #[test]
741 fn properties_test_animation_direction_triggered_by_set() {
742 let compo = Component::new_test_component();
743
744 let animation_details = PropertyAnimation {
745 delay: -25,
746 duration: DURATION.as_millis() as _,
747 direction: AnimationDirection::AlternateReverse,
748 iteration_count: 1.,
749 ..PropertyAnimation::default()
750 };
751
752 compo.width.set(100);
753 assert_eq!(get_prop_value(&compo.width), 100);
754 assert_eq!(get_prop_value(&compo.width_times_two), 200);
755
756 let start_time = crate::animations::current_tick();
757
758 compo.width.set_animated_value(200, animation_details);
759 assert_eq!(get_prop_value(&compo.width), 200);
760 assert_eq!(get_prop_value(&compo.width_times_two), 400);
761
762 crate::animations::CURRENT_ANIMATION_DRIVER
763 .with(|driver| driver.update_animations(start_time + DURATION / 2));
764 assert_eq!(get_prop_value(&compo.width), 150);
765 assert_eq!(get_prop_value(&compo.width_times_two), 300);
766
767 crate::animations::CURRENT_ANIMATION_DRIVER
768 .with(|driver| driver.update_animations(start_time + DURATION));
769 assert_eq!(get_prop_value(&compo.width), 100);
770 assert_eq!(get_prop_value(&compo.width_times_two), 200);
771
772 crate::animations::CURRENT_ANIMATION_DRIVER
774 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
775 assert_eq!(get_prop_value(&compo.width), 100);
776 assert_eq!(get_prop_value(&compo.width_times_two), 200);
777
778 compo.width.handle.access(|binding| assert!(binding.is_none()));
780 }
781
782 #[test]
783 fn properties_test_animation_triggered_by_binding() {
784 let compo = Component::new_test_component();
785
786 let start_time = crate::animations::current_tick();
787
788 let animation_details = PropertyAnimation {
789 duration: DURATION.as_millis() as _,
790 iteration_count: 1.,
791 ..PropertyAnimation::default()
792 };
793
794 let w = Rc::downgrade(&compo);
795 compo.width.set_animated_binding(
796 move || {
797 let compo = w.upgrade().unwrap();
798 get_prop_value(&compo.feed_property)
799 },
800 move || (animation_details.clone(), None),
801 );
802
803 compo.feed_property.set(100);
804 assert_eq!(get_prop_value(&compo.width), 100);
805 assert_eq!(get_prop_value(&compo.width_times_two), 200);
806
807 compo.feed_property.set(200);
808 assert_eq!(get_prop_value(&compo.width), 100);
809 assert_eq!(get_prop_value(&compo.width_times_two), 200);
810
811 crate::animations::CURRENT_ANIMATION_DRIVER
812 .with(|driver| driver.update_animations(start_time + DURATION / 2));
813 assert_eq!(get_prop_value(&compo.width), 150);
814 assert_eq!(get_prop_value(&compo.width_times_two), 300);
815
816 crate::animations::CURRENT_ANIMATION_DRIVER
817 .with(|driver| driver.update_animations(start_time + DURATION));
818 assert_eq!(get_prop_value(&compo.width), 200);
819 assert_eq!(get_prop_value(&compo.width_times_two), 400);
820 }
821
822 #[test]
823 fn properties_test_delayed_animation_triggered_by_binding() {
824 let compo = Component::new_test_component();
825
826 let start_time = crate::animations::current_tick();
827
828 let animation_details = PropertyAnimation {
829 delay: DELAY.as_millis() as _,
830 duration: DURATION.as_millis() as _,
831 iteration_count: 1.0,
832 ..PropertyAnimation::default()
833 };
834
835 let w = Rc::downgrade(&compo);
836 compo.width.set_animated_binding(
837 move || {
838 let compo = w.upgrade().unwrap();
839 get_prop_value(&compo.feed_property)
840 },
841 move || (animation_details.clone(), None),
842 );
843
844 compo.feed_property.set(100);
845 assert_eq!(get_prop_value(&compo.width), 100);
846 assert_eq!(get_prop_value(&compo.width_times_two), 200);
847
848 compo.feed_property.set(200);
849 assert_eq!(get_prop_value(&compo.width), 100);
850 assert_eq!(get_prop_value(&compo.width_times_two), 200);
851
852 crate::animations::CURRENT_ANIMATION_DRIVER
854 .with(|driver| driver.update_animations(start_time + DELAY / 2));
855 assert_eq!(get_prop_value(&compo.width), 100);
856 assert_eq!(get_prop_value(&compo.width_times_two), 200);
857
858 crate::animations::CURRENT_ANIMATION_DRIVER
860 .with(|driver| driver.update_animations(start_time + DELAY));
861 assert_eq!(get_prop_value(&compo.width), 100);
862 assert_eq!(get_prop_value(&compo.width_times_two), 200);
863
864 crate::animations::CURRENT_ANIMATION_DRIVER
865 .with(|driver| driver.update_animations(start_time + DELAY + DURATION / 2));
866 assert_eq!(get_prop_value(&compo.width), 150);
867 assert_eq!(get_prop_value(&compo.width_times_two), 300);
868
869 crate::animations::CURRENT_ANIMATION_DRIVER
870 .with(|driver| driver.update_animations(start_time + DELAY + DURATION));
871 assert_eq!(get_prop_value(&compo.width), 200);
872 assert_eq!(get_prop_value(&compo.width_times_two), 400);
873
874 crate::animations::CURRENT_ANIMATION_DRIVER
876 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
877 assert_eq!(get_prop_value(&compo.width), 200);
878 assert_eq!(get_prop_value(&compo.width_times_two), 400);
879 }
880
881 #[test]
882 fn test_loop() {
883 let compo = Component::new_test_component();
884
885 let animation_details = PropertyAnimation {
886 duration: DURATION.as_millis() as _,
887 iteration_count: 2.,
888 ..PropertyAnimation::default()
889 };
890
891 compo.width.set(100);
892
893 let start_time = crate::animations::current_tick();
894
895 compo.width.set_animated_value(200, animation_details);
896 assert_eq!(get_prop_value(&compo.width), 100);
897
898 crate::animations::CURRENT_ANIMATION_DRIVER
899 .with(|driver| driver.update_animations(start_time + DURATION / 2));
900 assert_eq!(get_prop_value(&compo.width), 150);
901
902 crate::animations::CURRENT_ANIMATION_DRIVER
903 .with(|driver| driver.update_animations(start_time + DURATION));
904 assert_eq!(get_prop_value(&compo.width), 100);
905
906 crate::animations::CURRENT_ANIMATION_DRIVER
907 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
908 assert_eq!(get_prop_value(&compo.width), 150);
909
910 crate::animations::CURRENT_ANIMATION_DRIVER
911 .with(|driver| driver.update_animations(start_time + DURATION * 2));
912 assert_eq!(get_prop_value(&compo.width), 200);
913
914 compo.width.handle.access(|binding| assert!(binding.is_none()));
916 }
917
918 #[test]
919 fn test_loop_via_binding() {
920 let compo = Component::new_test_component();
923
924 let start_time = crate::animations::current_tick();
925
926 let animation_details = PropertyAnimation {
927 duration: DURATION.as_millis() as _,
928 iteration_count: 2.,
929 ..PropertyAnimation::default()
930 };
931
932 let w = Rc::downgrade(&compo);
933 compo.width.set_animated_binding(
934 move || {
935 let compo = w.upgrade().unwrap();
936 get_prop_value(&compo.feed_property)
937 },
938 move || (animation_details.clone(), None),
939 );
940
941 compo.feed_property.set(100);
942 assert_eq!(get_prop_value(&compo.width), 100);
943
944 compo.feed_property.set(200);
945 assert_eq!(get_prop_value(&compo.width), 100);
946
947 crate::animations::CURRENT_ANIMATION_DRIVER
948 .with(|driver| driver.update_animations(start_time + DURATION / 2));
949
950 assert_eq!(get_prop_value(&compo.width), 150);
951
952 crate::animations::CURRENT_ANIMATION_DRIVER
953 .with(|driver| driver.update_animations(start_time + DURATION));
954
955 assert_eq!(get_prop_value(&compo.width), 100);
956
957 crate::animations::CURRENT_ANIMATION_DRIVER
958 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
959
960 assert_eq!(get_prop_value(&compo.width), 150);
961
962 crate::animations::CURRENT_ANIMATION_DRIVER
963 .with(|driver| driver.update_animations(start_time + 2 * DURATION));
964
965 assert_eq!(get_prop_value(&compo.width), 200);
966
967 crate::animations::CURRENT_ANIMATION_DRIVER
969 .with(|driver| driver.update_animations(start_time + 2 * DURATION + DURATION / 2));
970
971 assert_eq!(get_prop_value(&compo.width), 200);
972
973 let start_time = crate::animations::current_tick();
976
977 compo.feed_property.set(300);
978 assert_eq!(get_prop_value(&compo.width), 200);
979
980 crate::animations::CURRENT_ANIMATION_DRIVER
981 .with(|driver| driver.update_animations(start_time + DURATION / 2));
982
983 assert_eq!(get_prop_value(&compo.width), 250);
984
985 crate::animations::CURRENT_ANIMATION_DRIVER
986 .with(|driver| driver.update_animations(start_time + DURATION));
987
988 assert_eq!(get_prop_value(&compo.width), 200);
989
990 crate::animations::CURRENT_ANIMATION_DRIVER
991 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
992
993 assert_eq!(get_prop_value(&compo.width), 250);
994
995 crate::animations::CURRENT_ANIMATION_DRIVER
996 .with(|driver| driver.update_animations(start_time + 2 * DURATION));
997
998 assert_eq!(get_prop_value(&compo.width), 300);
999
1000 crate::animations::CURRENT_ANIMATION_DRIVER
1001 .with(|driver| driver.update_animations(start_time + 2 * DURATION + DURATION / 2));
1002
1003 assert_eq!(get_prop_value(&compo.width), 300);
1004 }
1005}