1use super::*;
5use crate::{
6 animations::physics_simulation::{self, Simulation},
7 items::{AnimationDirection, PropertyAnimation},
8 lengths::LogicalLength,
9};
10use euclid::Length;
11#[cfg(not(feature = "std"))]
12use num_traits::Float;
13
14enum AnimationState {
15 Delaying,
17 Animating {
19 current_iteration: u64,
20 },
21 Done {
22 iteration_count: u64,
23 },
24}
25
26pub(super) struct PropertyPhysicsAnimationData<S> {
27 simulation: S,
28 state: AnimationState,
29}
30
31impl<S> PropertyPhysicsAnimationData<S>
32where
33 S: physics_simulation::Simulation,
34{
35 pub fn new(simulation: S) -> PropertyPhysicsAnimationData<S> {
36 PropertyPhysicsAnimationData { simulation, state: AnimationState::Delaying }
37 }
38
39 pub fn update_value(&mut self, target: &mut crate::Coord) -> bool {
41 match self.state {
42 AnimationState::Delaying => {
43 self.state = AnimationState::Animating { current_iteration: 0 };
45 self.update_value(target)
46 }
47 AnimationState::Animating { current_iteration: _ } => {
48 let mut value: f32 = *target as f32;
50 let finished = self.simulation.step(&mut value, crate::animations::current_tick());
51 *target = value as crate::Coord;
52 if finished {
53 self.state = AnimationState::Done { iteration_count: 0 };
54 true
55 } else {
56 false
57 }
58 }
59 AnimationState::Done { iteration_count: _ } => true,
60 }
61 }
62}
63
64pub(super) struct PropertyValueAnimationData<T> {
65 from_value: T,
66 to_value: T,
67 details: PropertyAnimation,
68 start_time: crate::animations::Instant,
69 state: AnimationState,
70}
71
72impl<T: InterpolatedPropertyValue + Clone> PropertyValueAnimationData<T> {
73 pub fn new(from_value: T, to_value: T, details: PropertyAnimation) -> Self {
74 let start_time = crate::animations::current_tick();
75
76 Self { from_value, to_value, details, start_time, state: AnimationState::Delaying }
77 }
78
79 pub fn compute_interpolated_value(&mut self) -> (T, bool) {
81 if !self.details.enabled {
83 return (self.to_value.clone(), true);
84 }
85
86 let new_tick = crate::animations::current_tick();
87 let mut time_progress = new_tick.duration_since(self.start_time).as_millis() as u64;
88 let reversed = |iteration: u64| -> bool {
89 #[allow(clippy::manual_is_multiple_of)] match self.details.direction {
91 AnimationDirection::Normal => false,
92 AnimationDirection::Reverse => true,
93 AnimationDirection::Alternate => iteration % 2 == 1,
94 AnimationDirection::AlternateReverse => iteration % 2 == 0,
95 }
96 };
97
98 match self.state {
99 AnimationState::Delaying => {
100 if self.details.delay <= 0 {
101 self.state = AnimationState::Animating { current_iteration: 0 };
102 return self.compute_interpolated_value();
103 }
104
105 let delay = self.details.delay as u64;
106
107 if time_progress < delay {
108 if reversed(0) {
109 (self.to_value.clone(), false)
110 } else {
111 (self.from_value.clone(), false)
112 }
113 } else {
114 self.start_time =
115 new_tick - core::time::Duration::from_millis(time_progress - delay);
116
117 self.state = AnimationState::Animating { current_iteration: 0 };
119 self.compute_interpolated_value()
120 }
121 }
122 AnimationState::Animating { mut current_iteration } => {
123 if self.details.duration <= 0 || self.details.iteration_count == 0. {
124 self.state = AnimationState::Done { iteration_count: 0 };
125 return self.compute_interpolated_value();
126 }
127
128 let duration = self.details.duration as u64;
129 if time_progress >= duration {
130 current_iteration += time_progress / duration;
132 time_progress %= duration;
133 self.start_time = new_tick - core::time::Duration::from_millis(time_progress);
134 }
135
136 if (self.details.iteration_count < 0.)
137 || (((current_iteration * duration) + time_progress) as f64)
138 < ((self.details.iteration_count as f64) * (duration as f64))
139 {
140 self.state = AnimationState::Animating { current_iteration };
141
142 let progress = {
143 let progress =
144 (time_progress as f32 / self.details.duration as f32).clamp(0., 1.);
145 if reversed(current_iteration) { 1. - progress } else { progress }
146 };
147 let t = crate::animations::easing_curve(&self.details.easing, progress);
148 let val = self.from_value.interpolate(&self.to_value, t);
149
150 (val, false)
151 } else {
152 self.state =
153 AnimationState::Done { iteration_count: current_iteration.max(1) - 1 };
154 self.compute_interpolated_value()
155 }
156 }
157 AnimationState::Done { iteration_count } => {
158 if reversed(iteration_count) {
159 (self.from_value.clone(), true)
160 } else {
161 (self.to_value.clone(), true)
162 }
163 }
164 }
165 }
166
167 fn reset(&mut self) {
168 self.state = AnimationState::Delaying;
169 self.start_time = crate::animations::current_tick();
170 }
171}
172
173#[derive(Clone, Copy, Eq, PartialEq, Debug)]
174pub(super) enum AnimatedBindingState {
175 Animating,
176 NotAnimating,
177 ShouldStart,
178}
179
180#[pin_project::pin_project]
181pub(super) struct AnimatedBindingCallable<T, A> {
182 #[pin]
183 pub(super) original_binding: PropertyHandle,
184 pub(super) state: Cell<AnimatedBindingState>,
185 pub(super) animation_data: RefCell<PropertyValueAnimationData<T>>,
186 pub(super) compute_animation_details: A,
187}
188
189pub(super) type AnimationDetail = (PropertyAnimation, Option<crate::animations::Instant>);
190
191unsafe impl<T: InterpolatedPropertyValue + Clone, A: Fn() -> AnimationDetail> BindingCallable<T>
192 for AnimatedBindingCallable<T, A>
193{
194 fn evaluate(self: Pin<&Self>, value: &mut T) -> BindingResult {
195 let original_binding = self.project_ref().original_binding;
196 original_binding.register_as_dependency_to_current_binding(
197 #[cfg(slint_debug_property)]
198 "<AnimatedBindingCallable>",
199 );
200 match self.state.get() {
201 AnimatedBindingState::Animating => {
202 let (val, finished) = self.animation_data.borrow_mut().compute_interpolated_value();
203 *value = val;
204 if finished {
205 self.state.set(AnimatedBindingState::NotAnimating)
206 } else {
207 crate::animations::CURRENT_ANIMATION_DRIVER
208 .with(|driver| driver.set_has_active_animations());
209 }
210 }
211 AnimatedBindingState::NotAnimating => {
212 unsafe { self.original_binding.update(value as *mut T) };
214 }
215 AnimatedBindingState::ShouldStart => {
216 self.state.set(AnimatedBindingState::Animating);
217 let mut animation_data = self.animation_data.borrow_mut();
218 animation_data.from_value = value.clone();
220 let (details, start_time) = (self.compute_animation_details)();
221 if let Some(start_time) = start_time {
222 animation_data.start_time = start_time;
223 }
224 animation_data.details = details;
225
226 unsafe { self.original_binding.update((&mut animation_data.to_value) as *mut T) };
228 let (val, finished) = animation_data.compute_interpolated_value();
229 *value = val;
230 if finished {
231 self.state.set(AnimatedBindingState::NotAnimating)
232 } else {
233 crate::animations::CURRENT_ANIMATION_DRIVER
234 .with(|driver| driver.set_has_active_animations());
235 }
236 }
237 };
238 BindingResult::KeepBinding
239 }
240 fn mark_dirty(self: Pin<&Self>) {
241 if self.state.get() == AnimatedBindingState::ShouldStart {
242 return;
243 }
244 let original_dirty = self.original_binding.access(|b| b.unwrap().dirty.get());
245 if original_dirty {
246 self.state.set(AnimatedBindingState::ShouldStart);
247 self.animation_data.borrow_mut().reset();
248 }
249 }
250}
251
252pub trait InterpolatedPropertyValue: PartialEq + Default + 'static {
256 #[must_use]
260 fn interpolate(&self, target_value: &Self, t: f32) -> Self;
261}
262
263impl InterpolatedPropertyValue for f32 {
264 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
265 self + t * (target_value - self)
266 }
267}
268
269impl InterpolatedPropertyValue for i32 {
270 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
271 self + (t * (target_value - self) as f32).round() as i32
272 }
273}
274
275impl InterpolatedPropertyValue for i64 {
276 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
277 self + (t * (target_value - self) as f32).round() as Self
278 }
279}
280
281impl InterpolatedPropertyValue for u8 {
282 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
283 ((*self as f32) + (t * ((*target_value as f32) - (*self as f32)))).round().clamp(0., 255.)
284 as u8
285 }
286}
287
288impl InterpolatedPropertyValue for LogicalLength {
289 fn interpolate(&self, target_value: &Self, t: f32) -> Self {
290 LogicalLength::new(self.get().interpolate(&target_value.get(), t))
291 }
292}
293
294impl<T: Clone + InterpolatedPropertyValue + 'static> Property<T> {
295 pub fn remove_binding(self: Pin<&Self>) {
300 self.set_animated_value(self.get(), PropertyAnimation::default());
305 }
306
307 pub fn set_animated_value(&self, value: T, animation_data: PropertyAnimation) {
314 let d = RefCell::new(properties_animations::PropertyValueAnimationData::new(
316 self.get_internal(),
317 value,
318 animation_data,
319 ));
320 unsafe {
322 self.handle.set_binding(
323 move |val: &mut T| {
324 let (value, finished) = d.borrow_mut().compute_interpolated_value();
325 *val = value;
326 if finished {
327 BindingResult::RemoveBinding
328 } else {
329 crate::animations::CURRENT_ANIMATION_DRIVER
330 .with(|driver| driver.set_has_active_animations());
331 BindingResult::KeepBinding
332 }
333 },
334 #[cfg(slint_debug_property)]
335 self.debug_name.borrow().as_str(),
336 );
337 }
338 self.handle.mark_dirty(
339 #[cfg(slint_debug_property)]
340 self.debug_name.borrow().as_str(),
341 );
342 }
343
344 pub fn set_animated_binding(
347 &self,
348 binding: impl Binding<T> + 'static,
349 compute_animation_details: impl Fn() -> (PropertyAnimation, Option<crate::animations::Instant>)
350 + 'static,
351 ) {
352 let binding_callable = properties_animations::AnimatedBindingCallable::<T, _> {
353 original_binding: PropertyHandle {
354 handle: Cell::new(
355 (alloc_binding_holder(move |val: &mut T| {
356 *val = binding.evaluate(val);
357 BindingResult::KeepBinding
358 }) as *mut ())
359 .map_addr(|a| a | 0b10),
360 ),
361 },
362 state: Cell::new(properties_animations::AnimatedBindingState::NotAnimating),
363 animation_data: RefCell::new(properties_animations::PropertyValueAnimationData::new(
364 T::default(),
365 T::default(),
366 PropertyAnimation::default(),
367 )),
368 compute_animation_details,
369 };
370
371 unsafe {
373 self.handle.set_binding(
374 binding_callable,
375 #[cfg(slint_debug_property)]
376 self.debug_name.borrow().as_str(),
377 )
378 };
379 self.handle.mark_dirty(
380 #[cfg(slint_debug_property)]
381 self.debug_name.borrow().as_str(),
382 );
383 }
384}
385
386unsafe impl<Unit, S: Simulation> BindingCallable<Length<crate::Coord, Unit>>
387 for RefCell<PropertyPhysicsAnimationData<S>>
388{
389 fn evaluate(self: Pin<&Self>, value: &mut Length<crate::Coord, Unit>) -> BindingResult {
390 let finished = self.borrow_mut().update_value(&mut value.0);
391 if finished {
392 BindingResult::RemoveBinding
393 } else {
394 crate::animations::CURRENT_ANIMATION_DRIVER
395 .with(|driver| driver.set_has_active_animations());
396 BindingResult::KeepBinding
397 }
398 }
399
400 fn intercept_set(self: Pin<&Self>, _value: &Length<crate::Coord, Unit>) -> bool {
402 true
403 }
404}
405
406impl<Unit> Property<Length<crate::Coord, Unit>> {
407 pub fn set_physic_animation_value<
409 S: physics_simulation::Simulation + 'static,
410 AD: physics_simulation::Parameter<Output = S>,
411 >(
412 &self,
413 limit_value: Pin<Box<Property<f32>>>,
414 simulation_data: AD,
415 ) {
416 unsafe {
418 self.handle.set_binding::<Length<crate::Coord, Unit>, core::cell::RefCell<PropertyPhysicsAnimationData<S>>>(RefCell::new(PropertyPhysicsAnimationData::new(
419 simulation_data.simulation(self.get_internal().0 as f32, limit_value),
420 )),
421 #[cfg(slint_debug_property)]
422 self.debug_name.borrow().as_str()
423 );
424 }
425 self.handle.mark_dirty(
426 #[cfg(slint_debug_property)]
427 self.debug_name.borrow().as_str(),
428 );
429 }
430}
431
432#[cfg(test)]
433mod animation_tests {
434 use super::*;
435 use std::rc::Rc;
436
437 #[derive(Default)]
438 struct Component {
439 width: Property<i32>,
440 width_times_two: Property<i32>,
441 feed_property: Property<i32>, }
443
444 impl Component {
445 fn new_test_component() -> Rc<Self> {
446 let compo = Rc::new(Component::default());
447 let w = Rc::downgrade(&compo);
448 compo.width_times_two.set_binding(move || {
449 let compo = w.upgrade().unwrap();
450 get_prop_value(&compo.width) * 2
451 });
452
453 compo
454 }
455 }
456
457 const DURATION: std::time::Duration = std::time::Duration::from_millis(10000);
458 const DELAY: std::time::Duration = std::time::Duration::from_millis(800);
459
460 fn get_prop_value<T: Clone>(prop: &Property<T>) -> T {
462 unsafe { Pin::new_unchecked(prop).get() }
463 }
464
465 #[test]
466 fn properties_test_animation_negative_delay_triggered_by_set() {
467 let compo = Component::new_test_component();
468
469 let animation_details = PropertyAnimation {
470 delay: -25,
471 duration: DURATION.as_millis() as _,
472 iteration_count: 1.,
473 ..PropertyAnimation::default()
474 };
475
476 compo.width.set(100);
477 assert_eq!(get_prop_value(&compo.width), 100);
478 assert_eq!(get_prop_value(&compo.width_times_two), 200);
479
480 let start_time = crate::animations::current_tick();
481
482 compo.width.set_animated_value(200, animation_details);
483 assert_eq!(get_prop_value(&compo.width), 100);
484 assert_eq!(get_prop_value(&compo.width_times_two), 200);
485
486 crate::animations::CURRENT_ANIMATION_DRIVER
487 .with(|driver| driver.update_animations(start_time + DURATION / 2));
488 assert_eq!(get_prop_value(&compo.width), 150);
489 assert_eq!(get_prop_value(&compo.width_times_two), 300);
490
491 crate::animations::CURRENT_ANIMATION_DRIVER
492 .with(|driver| driver.update_animations(start_time + DURATION));
493 assert_eq!(get_prop_value(&compo.width), 200);
494 assert_eq!(get_prop_value(&compo.width_times_two), 400);
495
496 crate::animations::CURRENT_ANIMATION_DRIVER
498 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
499 assert_eq!(get_prop_value(&compo.width), 200);
500 assert_eq!(get_prop_value(&compo.width_times_two), 400);
501
502 compo.width.handle.access(|binding| assert!(binding.is_none()));
504 }
505
506 #[test]
507 fn properties_test_animation_triggered_by_set() {
508 let compo = Component::new_test_component();
509
510 let animation_details = PropertyAnimation {
511 duration: DURATION.as_millis() as _,
512 iteration_count: 1.,
513 ..PropertyAnimation::default()
514 };
515
516 compo.width.set(100);
517 assert_eq!(get_prop_value(&compo.width), 100);
518 assert_eq!(get_prop_value(&compo.width_times_two), 200);
519
520 let start_time = crate::animations::current_tick();
521
522 compo.width.set_animated_value(200, animation_details);
523 assert_eq!(get_prop_value(&compo.width), 100);
524 assert_eq!(get_prop_value(&compo.width_times_two), 200);
525
526 crate::animations::CURRENT_ANIMATION_DRIVER
527 .with(|driver| driver.update_animations(start_time + DURATION / 2));
528 assert_eq!(get_prop_value(&compo.width), 150);
529 assert_eq!(get_prop_value(&compo.width_times_two), 300);
530
531 crate::animations::CURRENT_ANIMATION_DRIVER
532 .with(|driver| driver.update_animations(start_time + DURATION));
533 assert_eq!(get_prop_value(&compo.width), 200);
534 assert_eq!(get_prop_value(&compo.width_times_two), 400);
535
536 crate::animations::CURRENT_ANIMATION_DRIVER
538 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
539 assert_eq!(get_prop_value(&compo.width), 200);
540 assert_eq!(get_prop_value(&compo.width_times_two), 400);
541
542 compo.width.handle.access(|binding| assert!(binding.is_none()));
544 }
545
546 #[test]
547 fn properties_test_delayed_animation_triggered_by_set() {
548 let compo = Component::new_test_component();
549
550 let animation_details = PropertyAnimation {
551 delay: DELAY.as_millis() as _,
552 iteration_count: 1.,
553 duration: DURATION.as_millis() as _,
554 ..PropertyAnimation::default()
555 };
556
557 compo.width.set(100);
558 assert_eq!(get_prop_value(&compo.width), 100);
559 assert_eq!(get_prop_value(&compo.width_times_two), 200);
560
561 let start_time = crate::animations::current_tick();
562
563 compo.width.set_animated_value(200, animation_details);
564 assert_eq!(get_prop_value(&compo.width), 100);
565 assert_eq!(get_prop_value(&compo.width_times_two), 200);
566
567 crate::animations::CURRENT_ANIMATION_DRIVER
569 .with(|driver| driver.update_animations(start_time + DELAY / 2));
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));
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
580 .with(|driver| driver.update_animations(start_time + DELAY + DURATION / 2));
581 assert_eq!(get_prop_value(&compo.width), 150);
582 assert_eq!(get_prop_value(&compo.width_times_two), 300);
583
584 crate::animations::CURRENT_ANIMATION_DRIVER
585 .with(|driver| driver.update_animations(start_time + DELAY + DURATION));
586 assert_eq!(get_prop_value(&compo.width), 200);
587 assert_eq!(get_prop_value(&compo.width_times_two), 400);
588
589 crate::animations::CURRENT_ANIMATION_DRIVER
591 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
592 assert_eq!(get_prop_value(&compo.width), 200);
593 assert_eq!(get_prop_value(&compo.width_times_two), 400);
594
595 compo.width.handle.access(|binding| assert!(binding.is_none()));
597 }
598
599 #[test]
600 fn properties_test_delayed_animation_fractal_iteration_triggered_by_set() {
601 let compo = Component::new_test_component();
602
603 let animation_details = PropertyAnimation {
604 delay: DELAY.as_millis() as _,
605 iteration_count: 1.5,
606 duration: DURATION.as_millis() as _,
607 ..PropertyAnimation::default()
608 };
609
610 compo.width.set(100);
611 assert_eq!(get_prop_value(&compo.width), 100);
612 assert_eq!(get_prop_value(&compo.width_times_two), 200);
613
614 let start_time = crate::animations::current_tick();
615
616 compo.width.set_animated_value(200, animation_details);
617 assert_eq!(get_prop_value(&compo.width), 100);
618 assert_eq!(get_prop_value(&compo.width_times_two), 200);
619
620 crate::animations::CURRENT_ANIMATION_DRIVER
622 .with(|driver| driver.update_animations(start_time + DELAY / 2));
623 assert_eq!(get_prop_value(&compo.width), 100);
624 assert_eq!(get_prop_value(&compo.width_times_two), 200);
625
626 crate::animations::CURRENT_ANIMATION_DRIVER
628 .with(|driver| driver.update_animations(start_time + DELAY));
629 assert_eq!(get_prop_value(&compo.width), 100);
630 assert_eq!(get_prop_value(&compo.width_times_two), 200);
631
632 crate::animations::CURRENT_ANIMATION_DRIVER
633 .with(|driver| driver.update_animations(start_time + DELAY + DURATION / 2));
634 assert_eq!(get_prop_value(&compo.width), 150);
635 assert_eq!(get_prop_value(&compo.width_times_two), 300);
636
637 crate::animations::CURRENT_ANIMATION_DRIVER
638 .with(|driver| driver.update_animations(start_time + DELAY + DURATION));
639 assert_eq!(get_prop_value(&compo.width), 100);
640 assert_eq!(get_prop_value(&compo.width_times_two), 200);
641
642 crate::animations::CURRENT_ANIMATION_DRIVER
644 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 4));
645 assert_eq!(get_prop_value(&compo.width), 125);
646 assert_eq!(get_prop_value(&compo.width_times_two), 250);
647
648 crate::animations::CURRENT_ANIMATION_DRIVER
650 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
651 assert_eq!(get_prop_value(&compo.width), 200);
652 assert_eq!(get_prop_value(&compo.width_times_two), 400);
653
654 compo.width.handle.access(|binding| assert!(binding.is_none()));
656 }
657 #[test]
658 fn properties_test_delayed_animation_null_duration_triggered_by_set() {
659 let compo = Component::new_test_component();
660
661 let animation_details = PropertyAnimation {
662 delay: DELAY.as_millis() as _,
663 iteration_count: 1.0,
664 duration: 0,
665 ..PropertyAnimation::default()
666 };
667
668 compo.width.set(100);
669 assert_eq!(get_prop_value(&compo.width), 100);
670 assert_eq!(get_prop_value(&compo.width_times_two), 200);
671
672 let start_time = crate::animations::current_tick();
673
674 compo.width.set_animated_value(200, animation_details);
675 assert_eq!(get_prop_value(&compo.width), 100);
676 assert_eq!(get_prop_value(&compo.width_times_two), 200);
677
678 crate::animations::CURRENT_ANIMATION_DRIVER
680 .with(|driver| driver.update_animations(start_time + DELAY / 2));
681 assert_eq!(get_prop_value(&compo.width), 100);
682 assert_eq!(get_prop_value(&compo.width_times_two), 200);
683
684 crate::animations::CURRENT_ANIMATION_DRIVER
686 .with(|driver| driver.update_animations(start_time + DELAY));
687 assert_eq!(get_prop_value(&compo.width), 200);
688 assert_eq!(get_prop_value(&compo.width_times_two), 400);
689
690 crate::animations::CURRENT_ANIMATION_DRIVER
692 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
693 assert_eq!(get_prop_value(&compo.width), 200);
694 assert_eq!(get_prop_value(&compo.width_times_two), 400);
695
696 compo.width.handle.access(|binding| assert!(binding.is_none()));
698 }
699
700 #[test]
701 fn properties_test_delayed_animation_negative_duration_triggered_by_set() {
702 let compo = Component::new_test_component();
703
704 let animation_details = PropertyAnimation {
705 delay: DELAY.as_millis() as _,
706 iteration_count: 1.0,
707 duration: -25,
708 ..PropertyAnimation::default()
709 };
710
711 compo.width.set(100);
712 assert_eq!(get_prop_value(&compo.width), 100);
713 assert_eq!(get_prop_value(&compo.width_times_two), 200);
714
715 let start_time = crate::animations::current_tick();
716
717 compo.width.set_animated_value(200, animation_details);
718 assert_eq!(get_prop_value(&compo.width), 100);
719 assert_eq!(get_prop_value(&compo.width_times_two), 200);
720
721 crate::animations::CURRENT_ANIMATION_DRIVER
723 .with(|driver| driver.update_animations(start_time + DELAY / 2));
724 assert_eq!(get_prop_value(&compo.width), 100);
725 assert_eq!(get_prop_value(&compo.width_times_two), 200);
726
727 crate::animations::CURRENT_ANIMATION_DRIVER
729 .with(|driver| driver.update_animations(start_time + DELAY));
730 assert_eq!(get_prop_value(&compo.width), 200);
731 assert_eq!(get_prop_value(&compo.width_times_two), 400);
732
733 crate::animations::CURRENT_ANIMATION_DRIVER
735 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
736 assert_eq!(get_prop_value(&compo.width), 200);
737 assert_eq!(get_prop_value(&compo.width_times_two), 400);
738
739 compo.width.handle.access(|binding| assert!(binding.is_none()));
741 }
742
743 #[test]
744 fn properties_test_delayed_animation_no_iteration_triggered_by_set() {
745 let compo = Component::new_test_component();
746
747 let animation_details = PropertyAnimation {
748 delay: DELAY.as_millis() as _,
749 iteration_count: 0.0,
750 duration: DURATION.as_millis() as _,
751 ..PropertyAnimation::default()
752 };
753
754 compo.width.set(100);
755 assert_eq!(get_prop_value(&compo.width), 100);
756 assert_eq!(get_prop_value(&compo.width_times_two), 200);
757
758 let start_time = crate::animations::current_tick();
759
760 compo.width.set_animated_value(200, animation_details);
761 assert_eq!(get_prop_value(&compo.width), 100);
762 assert_eq!(get_prop_value(&compo.width_times_two), 200);
763
764 crate::animations::CURRENT_ANIMATION_DRIVER
766 .with(|driver| driver.update_animations(start_time + DELAY / 2));
767 assert_eq!(get_prop_value(&compo.width), 100);
768 assert_eq!(get_prop_value(&compo.width_times_two), 200);
769
770 crate::animations::CURRENT_ANIMATION_DRIVER
772 .with(|driver| driver.update_animations(start_time + DELAY));
773 assert_eq!(get_prop_value(&compo.width), 200);
774 assert_eq!(get_prop_value(&compo.width_times_two), 400);
775
776 crate::animations::CURRENT_ANIMATION_DRIVER
778 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
779 assert_eq!(get_prop_value(&compo.width), 200);
780 assert_eq!(get_prop_value(&compo.width_times_two), 400);
781
782 compo.width.handle.access(|binding| assert!(binding.is_none()));
784 }
785
786 #[test]
787 fn properties_test_delayed_animation_negative_iteration_triggered_by_set() {
788 let compo = Component::new_test_component();
789
790 let animation_details = PropertyAnimation {
791 delay: DELAY.as_millis() as _,
792 iteration_count: -42., duration: DURATION.as_millis() as _,
794 ..PropertyAnimation::default()
795 };
796
797 compo.width.set(100);
798 assert_eq!(get_prop_value(&compo.width), 100);
799 assert_eq!(get_prop_value(&compo.width_times_two), 200);
800
801 let start_time = crate::animations::current_tick();
802
803 compo.width.set_animated_value(200, animation_details);
804 assert_eq!(get_prop_value(&compo.width), 100);
805 assert_eq!(get_prop_value(&compo.width_times_two), 200);
806
807 crate::animations::CURRENT_ANIMATION_DRIVER
809 .with(|driver| driver.update_animations(start_time + DELAY / 2));
810 assert_eq!(get_prop_value(&compo.width), 100);
811 assert_eq!(get_prop_value(&compo.width_times_two), 200);
812
813 crate::animations::CURRENT_ANIMATION_DRIVER
815 .with(|driver| driver.update_animations(start_time + DELAY));
816 assert_eq!(get_prop_value(&compo.width), 100);
817 assert_eq!(get_prop_value(&compo.width_times_two), 200);
818
819 crate::animations::CURRENT_ANIMATION_DRIVER
820 .with(|driver| driver.update_animations(start_time + DELAY + DURATION / 2));
821 assert_eq!(get_prop_value(&compo.width), 150);
822 assert_eq!(get_prop_value(&compo.width_times_two), 300);
823
824 crate::animations::CURRENT_ANIMATION_DRIVER
825 .with(|driver| driver.update_animations(start_time + DELAY + DURATION));
826 assert_eq!(get_prop_value(&compo.width), 100);
827 assert_eq!(get_prop_value(&compo.width_times_two), 200);
828
829 crate::animations::CURRENT_ANIMATION_DRIVER
831 .with(|driver| driver.update_animations(start_time + DELAY + 500 * DURATION));
832 assert_eq!(get_prop_value(&compo.width), 100);
833 assert_eq!(get_prop_value(&compo.width_times_two), 200);
834
835 crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| {
836 driver.update_animations(start_time + DELAY + 50000 * DURATION + DURATION / 2)
837 });
838 assert_eq!(get_prop_value(&compo.width), 150);
839 assert_eq!(get_prop_value(&compo.width_times_two), 300);
840
841 compo.width.handle.access(|binding| assert!(binding.is_some()));
843 }
844
845 #[test]
846 fn properties_test_animation_direction_triggered_by_set() {
847 let compo = Component::new_test_component();
848
849 let animation_details = PropertyAnimation {
850 delay: -25,
851 duration: DURATION.as_millis() as _,
852 direction: AnimationDirection::AlternateReverse,
853 iteration_count: 1.,
854 ..PropertyAnimation::default()
855 };
856
857 compo.width.set(100);
858 assert_eq!(get_prop_value(&compo.width), 100);
859 assert_eq!(get_prop_value(&compo.width_times_two), 200);
860
861 let start_time = crate::animations::current_tick();
862
863 compo.width.set_animated_value(200, animation_details);
864 assert_eq!(get_prop_value(&compo.width), 200);
865 assert_eq!(get_prop_value(&compo.width_times_two), 400);
866
867 crate::animations::CURRENT_ANIMATION_DRIVER
868 .with(|driver| driver.update_animations(start_time + DURATION / 2));
869 assert_eq!(get_prop_value(&compo.width), 150);
870 assert_eq!(get_prop_value(&compo.width_times_two), 300);
871
872 crate::animations::CURRENT_ANIMATION_DRIVER
873 .with(|driver| driver.update_animations(start_time + DURATION));
874 assert_eq!(get_prop_value(&compo.width), 100);
875 assert_eq!(get_prop_value(&compo.width_times_two), 200);
876
877 crate::animations::CURRENT_ANIMATION_DRIVER
879 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
880 assert_eq!(get_prop_value(&compo.width), 100);
881 assert_eq!(get_prop_value(&compo.width_times_two), 200);
882
883 compo.width.handle.access(|binding| assert!(binding.is_none()));
885 }
886
887 #[test]
888 fn properties_test_animation_triggered_by_binding() {
889 let compo = Component::new_test_component();
890
891 let start_time = crate::animations::current_tick();
892
893 let animation_details = PropertyAnimation {
894 duration: DURATION.as_millis() as _,
895 iteration_count: 1.,
896 ..PropertyAnimation::default()
897 };
898
899 let w = Rc::downgrade(&compo);
900 compo.width.set_animated_binding(
901 move || {
902 let compo = w.upgrade().unwrap();
903 get_prop_value(&compo.feed_property)
904 },
905 move || (animation_details.clone(), None),
906 );
907
908 compo.feed_property.set(100);
909 assert_eq!(get_prop_value(&compo.width), 100);
910 assert_eq!(get_prop_value(&compo.width_times_two), 200);
911
912 compo.feed_property.set(200);
913 assert_eq!(get_prop_value(&compo.width), 100);
914 assert_eq!(get_prop_value(&compo.width_times_two), 200);
915
916 crate::animations::CURRENT_ANIMATION_DRIVER
917 .with(|driver| driver.update_animations(start_time + DURATION / 2));
918 assert_eq!(get_prop_value(&compo.width), 150);
919 assert_eq!(get_prop_value(&compo.width_times_two), 300);
920
921 crate::animations::CURRENT_ANIMATION_DRIVER
922 .with(|driver| driver.update_animations(start_time + DURATION));
923 assert_eq!(get_prop_value(&compo.width), 200);
924 assert_eq!(get_prop_value(&compo.width_times_two), 400);
925 }
926
927 #[test]
928 fn properties_test_delayed_animation_triggered_by_binding() {
929 let compo = Component::new_test_component();
930
931 let start_time = crate::animations::current_tick();
932
933 let animation_details = PropertyAnimation {
934 delay: DELAY.as_millis() as _,
935 duration: DURATION.as_millis() as _,
936 iteration_count: 1.0,
937 ..PropertyAnimation::default()
938 };
939
940 let w = Rc::downgrade(&compo);
941 compo.width.set_animated_binding(
942 move || {
943 let compo = w.upgrade().unwrap();
944 get_prop_value(&compo.feed_property)
945 },
946 move || (animation_details.clone(), None),
947 );
948
949 compo.feed_property.set(100);
950 assert_eq!(get_prop_value(&compo.width), 100);
951 assert_eq!(get_prop_value(&compo.width_times_two), 200);
952
953 compo.feed_property.set(200);
954 assert_eq!(get_prop_value(&compo.width), 100);
955 assert_eq!(get_prop_value(&compo.width_times_two), 200);
956
957 crate::animations::CURRENT_ANIMATION_DRIVER
959 .with(|driver| driver.update_animations(start_time + DELAY / 2));
960 assert_eq!(get_prop_value(&compo.width), 100);
961 assert_eq!(get_prop_value(&compo.width_times_two), 200);
962
963 crate::animations::CURRENT_ANIMATION_DRIVER
965 .with(|driver| driver.update_animations(start_time + DELAY));
966 assert_eq!(get_prop_value(&compo.width), 100);
967 assert_eq!(get_prop_value(&compo.width_times_two), 200);
968
969 crate::animations::CURRENT_ANIMATION_DRIVER
970 .with(|driver| driver.update_animations(start_time + DELAY + DURATION / 2));
971 assert_eq!(get_prop_value(&compo.width), 150);
972 assert_eq!(get_prop_value(&compo.width_times_two), 300);
973
974 crate::animations::CURRENT_ANIMATION_DRIVER
975 .with(|driver| driver.update_animations(start_time + DELAY + DURATION));
976 assert_eq!(get_prop_value(&compo.width), 200);
977 assert_eq!(get_prop_value(&compo.width_times_two), 400);
978
979 crate::animations::CURRENT_ANIMATION_DRIVER
981 .with(|driver| driver.update_animations(start_time + DELAY + DURATION + DURATION / 2));
982 assert_eq!(get_prop_value(&compo.width), 200);
983 assert_eq!(get_prop_value(&compo.width_times_two), 400);
984 }
985
986 #[test]
987 fn test_loop() {
988 let compo = Component::new_test_component();
989
990 let animation_details = PropertyAnimation {
991 duration: DURATION.as_millis() as _,
992 iteration_count: 2.,
993 ..PropertyAnimation::default()
994 };
995
996 compo.width.set(100);
997
998 let start_time = crate::animations::current_tick();
999
1000 compo.width.set_animated_value(200, animation_details);
1001 assert_eq!(get_prop_value(&compo.width), 100);
1002
1003 crate::animations::CURRENT_ANIMATION_DRIVER
1004 .with(|driver| driver.update_animations(start_time + DURATION / 2));
1005 assert_eq!(get_prop_value(&compo.width), 150);
1006
1007 crate::animations::CURRENT_ANIMATION_DRIVER
1008 .with(|driver| driver.update_animations(start_time + DURATION));
1009 assert_eq!(get_prop_value(&compo.width), 100);
1010
1011 crate::animations::CURRENT_ANIMATION_DRIVER
1012 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
1013 assert_eq!(get_prop_value(&compo.width), 150);
1014
1015 crate::animations::CURRENT_ANIMATION_DRIVER
1016 .with(|driver| driver.update_animations(start_time + DURATION * 2));
1017 assert_eq!(get_prop_value(&compo.width), 200);
1018
1019 compo.width.handle.access(|binding| assert!(binding.is_none()));
1021 }
1022
1023 #[test]
1024 fn test_loop_via_binding() {
1025 let compo = Component::new_test_component();
1028
1029 let start_time = crate::animations::current_tick();
1030
1031 let animation_details = PropertyAnimation {
1032 duration: DURATION.as_millis() as _,
1033 iteration_count: 2.,
1034 ..PropertyAnimation::default()
1035 };
1036
1037 let w = Rc::downgrade(&compo);
1038 compo.width.set_animated_binding(
1039 move || {
1040 let compo = w.upgrade().unwrap();
1041 get_prop_value(&compo.feed_property)
1042 },
1043 move || (animation_details.clone(), None),
1044 );
1045
1046 compo.feed_property.set(100);
1047 assert_eq!(get_prop_value(&compo.width), 100);
1048
1049 compo.feed_property.set(200);
1050 assert_eq!(get_prop_value(&compo.width), 100);
1051
1052 crate::animations::CURRENT_ANIMATION_DRIVER
1053 .with(|driver| driver.update_animations(start_time + DURATION / 2));
1054
1055 assert_eq!(get_prop_value(&compo.width), 150);
1056
1057 crate::animations::CURRENT_ANIMATION_DRIVER
1058 .with(|driver| driver.update_animations(start_time + DURATION));
1059
1060 assert_eq!(get_prop_value(&compo.width), 100);
1061
1062 crate::animations::CURRENT_ANIMATION_DRIVER
1063 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
1064
1065 assert_eq!(get_prop_value(&compo.width), 150);
1066
1067 crate::animations::CURRENT_ANIMATION_DRIVER
1068 .with(|driver| driver.update_animations(start_time + 2 * DURATION));
1069
1070 assert_eq!(get_prop_value(&compo.width), 200);
1071
1072 crate::animations::CURRENT_ANIMATION_DRIVER
1074 .with(|driver| driver.update_animations(start_time + 2 * DURATION + DURATION / 2));
1075
1076 assert_eq!(get_prop_value(&compo.width), 200);
1077
1078 let start_time = crate::animations::current_tick();
1081
1082 compo.feed_property.set(300);
1083 assert_eq!(get_prop_value(&compo.width), 200);
1084
1085 crate::animations::CURRENT_ANIMATION_DRIVER
1086 .with(|driver| driver.update_animations(start_time + DURATION / 2));
1087
1088 assert_eq!(get_prop_value(&compo.width), 250);
1089
1090 crate::animations::CURRENT_ANIMATION_DRIVER
1091 .with(|driver| driver.update_animations(start_time + DURATION));
1092
1093 assert_eq!(get_prop_value(&compo.width), 200);
1094
1095 crate::animations::CURRENT_ANIMATION_DRIVER
1096 .with(|driver| driver.update_animations(start_time + DURATION + DURATION / 2));
1097
1098 assert_eq!(get_prop_value(&compo.width), 250);
1099
1100 crate::animations::CURRENT_ANIMATION_DRIVER
1101 .with(|driver| driver.update_animations(start_time + 2 * DURATION));
1102
1103 assert_eq!(get_prop_value(&compo.width), 300);
1104
1105 crate::animations::CURRENT_ANIMATION_DRIVER
1106 .with(|driver| driver.update_animations(start_time + 2 * DURATION + DURATION / 2));
1107
1108 assert_eq!(get_prop_value(&compo.width), 300);
1109 }
1110}