1use std::{
2 ops::Deref,
3 time::{
4 Duration,
5 Instant,
6 },
7};
8
9use async_io::Timer;
10use freya_core::prelude::*;
11
12#[derive(Default, PartialEq, Clone, Debug)]
13pub struct AnimConfiguration {
14 on_finish: OnFinish,
15 on_creation: OnCreation,
16 on_change: OnChange,
17}
18
19impl AnimConfiguration {
20 pub fn on_finish(&mut self, on_finish: OnFinish) -> &mut Self {
21 self.on_finish = on_finish;
22 self
23 }
24
25 pub fn on_creation(&mut self, on_creation: OnCreation) -> &mut Self {
26 self.on_creation = on_creation;
27 self
28 }
29
30 pub fn on_change(&mut self, on_change: OnChange) -> &mut Self {
31 self.on_change = on_change;
32 self
33 }
34}
35
36#[derive(Clone, Copy, PartialEq)]
38pub enum AnimDirection {
39 Forward,
40 Reverse,
41}
42
43impl AnimDirection {
44 pub fn toggle(&mut self) {
45 match self {
46 Self::Forward => *self = Self::Reverse,
47 Self::Reverse => *self = Self::Forward,
48 }
49 }
50}
51
52#[derive(PartialEq, Clone, Copy, Default, Debug)]
56pub enum OnFinish {
57 #[default]
59 Nothing,
60 Reverse {
62 delay: Duration,
64 },
65 Restart {
67 delay: Duration,
69 },
70}
71
72impl OnFinish {
73 pub fn nothing() -> Self {
75 Self::Nothing
76 }
77
78 pub fn reverse() -> Self {
80 Self::Reverse {
81 delay: Duration::ZERO,
82 }
83 }
84
85 pub fn reverse_with_delay(delay: Duration) -> Self {
87 Self::Reverse { delay }
88 }
89
90 pub fn restart() -> Self {
92 Self::Restart {
93 delay: Duration::ZERO,
94 }
95 }
96
97 pub fn restart_with_delay(delay: Duration) -> Self {
99 Self::Restart { delay }
100 }
101}
102
103#[derive(PartialEq, Clone, Copy, Default, Debug)]
107pub enum OnCreation {
108 #[default]
110 Nothing,
111 Run,
113 Finish,
115}
116
117#[derive(PartialEq, Clone, Copy, Default, Debug)]
121pub enum OnChange {
122 #[default]
124 Reset,
125 Finish,
127 Rerun,
129 Nothing,
131}
132
133pub trait ReadAnimatedValue: Clone + 'static {
134 type Output;
135 fn value(&self) -> Self::Output;
136}
137
138pub trait AnimatedValue: Clone + Default + 'static {
139 fn prepare(&mut self, direction: AnimDirection);
140
141 fn is_finished(&self, index: u128, direction: AnimDirection) -> bool;
142
143 fn advance(&mut self, index: u128, direction: AnimDirection);
144
145 fn finish(&mut self, direction: AnimDirection);
146
147 fn into_reversed(self) -> Self;
148}
149
150#[derive(Default, Clone, Copy, PartialEq, Eq)]
151pub enum Ease {
152 In,
153 #[default]
154 Out,
155 InOut,
156}
157
158#[derive(Clone, PartialEq)]
160pub struct UseAnimation<Animated: AnimatedValue> {
161 pub(crate) animated_value: State<Animated>,
162 pub(crate) config: State<AnimConfiguration>,
163 pub(crate) is_running: State<bool>,
164 pub(crate) has_run_yet: State<bool>,
165 pub(crate) runs: State<usize>,
166 pub(crate) task: State<Option<TaskHandle>>,
167 pub(crate) last_direction: State<AnimDirection>,
168}
169impl<T: AnimatedValue> Copy for UseAnimation<T> {}
170
171impl<Animated: AnimatedValue> Deref for UseAnimation<Animated> {
172 type Target = State<Animated>;
173 fn deref(&self) -> &Self::Target {
174 &self.animated_value
175 }
176}
177
178impl<Animated: AnimatedValue> UseAnimation<Animated> {
179 pub fn get(&self) -> ReadRef<'static, Animated> {
181 self.animated_value.read()
182 }
183
184 pub fn direction(&self) -> ReadRef<'static, AnimDirection> {
186 self.last_direction.read()
187 }
188
189 pub fn start(&mut self) {
191 self.run(AnimDirection::Forward)
192 }
193
194 pub fn reverse(&mut self) {
196 self.run(AnimDirection::Reverse)
197 }
198
199 pub fn reset(&mut self) {
201 if let Some(task) = self.task.write().take() {
202 task.cancel();
203 }
204
205 self.animated_value
206 .write()
207 .prepare(*self.last_direction.peek());
208
209 *self.has_run_yet.write() = true;
210 }
211
212 pub fn finish(&mut self) {
214 if let Some(task) = self.task.write().take() {
215 task.cancel();
216 }
217
218 self.animated_value
219 .write()
220 .finish(*self.last_direction.peek());
221
222 *self.has_run_yet.write() = true;
223 }
224
225 pub fn is_running(&self) -> State<bool> {
226 self.is_running
227 }
228
229 pub fn has_run_yet(&self) -> State<bool> {
230 self.has_run_yet
231 }
232
233 pub fn runs(&self) -> State<usize> {
235 self.runs
236 }
237
238 pub fn run(&self, mut direction: AnimDirection) {
240 let mut is_running = self.is_running;
241 let mut has_run_yet = self.has_run_yet;
242 let mut runs = self.runs;
243 let mut task = self.task;
244 let mut last_direction = self.last_direction;
245
246 let on_finish = self.config.peek().on_finish;
247 let mut animated_value = self.animated_value;
248
249 last_direction.set(direction);
250
251 if let Some(task) = task.write().take() {
253 task.cancel();
254 }
255
256 let mut ticker = RenderingTicker::get();
257 let platform = Platform::get();
258 let animation_clock = AnimationClock::get();
259
260 animated_value.write().prepare(direction);
262
263 has_run_yet.set_if_modified(true);
264 is_running.set_if_modified(true);
265 *runs.write() += 1;
266
267 let animation_task = spawn(async move {
268 platform.send(UserEvent::RequestRedraw);
269
270 let mut index = 0u128;
271 let mut prev_frame = Instant::now();
272
273 loop {
274 ticker.tick().await;
276
277 platform.send(UserEvent::RequestRedraw);
279
280 let elapsed = animation_clock.correct_elapsed_duration(prev_frame.elapsed());
281
282 index += elapsed.as_millis();
283
284 let is_finished = {
285 let mut animated_value = animated_value.write();
286 let is_finished = animated_value.is_finished(index, direction);
287 animated_value.advance(index, direction);
289
290 is_finished
291 };
292
293 if is_finished {
294 let delay = match on_finish {
295 OnFinish::Reverse { delay } => {
296 direction.toggle();
298 delay
299 }
300 OnFinish::Restart { delay } => delay,
301 OnFinish::Nothing => {
302 break;
304 }
305 };
306
307 if !delay.is_zero() {
308 Timer::after(delay).await;
309 }
310
311 index = 0;
312
313 animated_value.write().prepare(direction);
315 }
316
317 prev_frame = Instant::now();
318 }
319
320 is_running.set(false);
321 task.write().take();
322 });
323
324 task.write().replace(animation_task);
326 }
327}
328pub fn use_animation<Animated: AnimatedValue>(
424 mut run: impl 'static + FnMut(&mut AnimConfiguration) -> Animated,
425) -> UseAnimation<Animated> {
426 use_hook(|| {
427 let mut config = State::create(AnimConfiguration::default());
428 let mut animated_value = State::create(Animated::default());
429 let is_running = State::create(false);
430 let has_run_yet = State::create(false);
431 let runs = State::create(0);
432 let task = State::create(None);
433 let last_direction = State::create(AnimDirection::Forward);
434
435 let mut animation = UseAnimation {
436 animated_value,
437 config,
438 is_running,
439 has_run_yet,
440 runs,
441 task,
442 last_direction,
443 };
444
445 Effect::create_sync_with_gen(move |current_gen| {
446 let mut anim_conf = AnimConfiguration::default();
447 animated_value.set(run(&mut anim_conf));
448 *config.write() = anim_conf;
449 match config.peek().on_change {
450 OnChange::Finish if current_gen > 0 => {
451 animation.finish();
452 }
453 OnChange::Rerun if current_gen > 0 => {
454 let last_direction = *animation.last_direction.peek();
455 animation.run(last_direction);
456 }
457 OnChange::Reset if current_gen > 0 => {
458 animation.reset();
459 }
460 _ => {}
461 }
462 });
463
464 match config.peek().on_creation {
465 OnCreation::Run => {
466 animation.run(AnimDirection::Forward);
467 }
468 OnCreation::Finish => {
469 animation.finish();
470 }
471 _ => {}
472 }
473
474 animation
475 })
476}
477
478pub fn use_animation_with_dependencies<Animated: AnimatedValue, D: 'static + Clone + PartialEq>(
495 dependencies: &D,
496 mut run: impl 'static + FnMut(&mut AnimConfiguration, &D) -> Animated,
497) -> UseAnimation<Animated> {
498 let dependencies = use_reactive(dependencies);
499 use_hook(|| {
500 let mut config = State::create(AnimConfiguration::default());
501 let mut animated_value = State::create(Animated::default());
502 let is_running = State::create(false);
503 let has_run_yet = State::create(false);
504 let runs = State::create(0);
505 let task = State::create(None);
506 let last_direction = State::create(AnimDirection::Forward);
507
508 let mut animation = UseAnimation {
509 animated_value,
510 config,
511 is_running,
512 has_run_yet,
513 runs,
514 task,
515 last_direction,
516 };
517
518 Effect::create_sync_with_gen(move |current_gen| {
519 let dependencies = dependencies.read();
520 let mut anim_conf = AnimConfiguration::default();
521 animated_value.set(run(&mut anim_conf, &dependencies));
522 *config.write() = anim_conf;
523
524 match config.peek().on_change {
525 OnChange::Finish if current_gen > 0 => {
526 animation.finish();
527 }
528 OnChange::Rerun if current_gen > 0 => {
529 let last_direction = *animation.last_direction.peek();
530 animation.run(last_direction);
531 }
532 OnChange::Reset if current_gen > 0 => {
533 animation.reset();
534 }
535 _ => {}
536 }
537 });
538
539 match config.peek().on_creation {
540 OnCreation::Run => {
541 animation.run(AnimDirection::Forward);
542 }
543 OnCreation::Finish => {
544 animation.finish();
545 }
546 _ => {}
547 }
548
549 animation
550 })
551}
552
553macro_rules! impl_tuple_call {
554 ($(($($type:ident),*)),*) => {
555 $(
556 impl<$($type,)*> AnimatedValue for ($($type,)*)
557 where
558 $($type: AnimatedValue,)*
559 {
560 fn prepare(&mut self, direction: AnimDirection) {
561 #[allow(non_snake_case)]
562 let ($($type,)*) = self;
563 $(
564 $type.prepare(direction);
565 )*
566 }
567
568 fn is_finished(&self, index: u128, direction: AnimDirection) -> bool {
569 #[allow(non_snake_case)]
570 let ($($type,)*) = self;
571 $(
572 if !$type.is_finished(index, direction) {
573 return false;
574 }
575 )*
576 true
577 }
578
579 fn advance(&mut self, index: u128, direction: AnimDirection) {
580 #[allow(non_snake_case)]
581 let ($($type,)*) = self;
582 $(
583 $type.advance(index, direction);
584 )*
585 }
586
587 fn finish(&mut self, direction: AnimDirection) {
588 #[allow(non_snake_case)]
589 let ($($type,)*) = self;
590 $(
591 $type.finish(direction);
592 )*
593 }
594
595 fn into_reversed(self) -> Self {
596 #[allow(non_snake_case)]
597 let ($($type,)*) = self;
598 (
599 $(
600 $type.into_reversed(),
601 )*
602 )
603 }
604 }
605 impl<$($type,)*> ReadAnimatedValue for ($($type,)*)
606 where
607 $($type: ReadAnimatedValue,)*
608 {
609 type Output = (
610 $(
611 <$type as ReadAnimatedValue>::Output,
612 )*
613 );
614 fn value(&self) -> Self::Output {
615 #[allow(non_snake_case)]
616 let ($($type,)*) = self;
617 (
618 $(
619 $type.value(),
620 )*
621 )
622 }
623 }
624 )*
625 };
626}
627
628impl_tuple_call!(
629 (T1),
630 (T1, T2),
631 (T1, T2, T3),
632 (T1, T2, T3, T4),
633 (T1, T2, T3, T4, T5),
634 (T1, T2, T3, T4, T5, T6),
635 (T1, T2, T3, T4, T5, T6, T7),
636 (T1, T2, T3, T4, T5, T6, T7, T8),
637 (T1, T2, T3, T4, T5, T6, T7, T8, T9),
638 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10),
639 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11),
640 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
641);
642
643pub fn use_animation_transition<Animated: AnimatedValue, T: Clone + PartialEq + 'static>(
665 value: impl IntoReadable<T>,
666 mut run: impl 'static + FnMut(T, T) -> Animated,
667) -> UseAnimation<Animated> {
668 let values = use_previous_and_current(value);
669 use_animation(move |conf| {
670 conf.on_change(OnChange::Rerun);
671 conf.on_creation(OnCreation::Run);
672 let (from, to) = values.read().clone();
673 run(from, to)
674 })
675}