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