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) task: State<Option<TaskHandle>>,
166 pub(crate) last_direction: State<AnimDirection>,
167}
168impl<T: AnimatedValue> Copy for UseAnimation<T> {}
169
170impl<Animated: AnimatedValue> Deref for UseAnimation<Animated> {
171 type Target = State<Animated>;
172 fn deref(&self) -> &Self::Target {
173 &self.animated_value
174 }
175}
176
177impl<Animated: AnimatedValue> UseAnimation<Animated> {
178 pub fn get(&self) -> ReadRef<'static, Animated> {
180 self.animated_value.read()
181 }
182
183 pub fn direction(&self) -> ReadRef<'static, AnimDirection> {
185 self.last_direction.read()
186 }
187
188 pub fn start(&mut self) {
190 self.run(AnimDirection::Forward)
191 }
192
193 pub fn reverse(&mut self) {
195 self.run(AnimDirection::Reverse)
196 }
197
198 pub fn reset(&mut self) {
200 if let Some(task) = self.task.write().take() {
201 task.cancel();
202 }
203
204 self.animated_value
205 .write()
206 .prepare(*self.last_direction.peek());
207
208 *self.has_run_yet.write() = true;
209 }
210
211 pub fn finish(&mut self) {
213 if let Some(task) = self.task.write().take() {
214 task.cancel();
215 }
216
217 self.animated_value
218 .write()
219 .finish(*self.last_direction.peek());
220
221 *self.has_run_yet.write() = true;
222 }
223
224 pub fn is_running(&self) -> State<bool> {
225 self.is_running
226 }
227
228 pub fn has_run_yet(&self) -> State<bool> {
229 self.has_run_yet
230 }
231
232 pub fn run(&self, mut direction: AnimDirection) {
234 let mut is_running = self.is_running;
235 let mut has_run_yet = self.has_run_yet;
236 let mut task = self.task;
237 let mut last_direction = self.last_direction;
238
239 let on_finish = self.config.peek().on_finish;
240 let mut animated_value = self.animated_value;
241
242 last_direction.set(direction);
243
244 if let Some(task) = task.write().take() {
246 task.cancel();
247 }
248
249 let peek_has_run_yet = *self.has_run_yet.peek();
250
251 let mut ticker = RenderingTicker::get();
252 let platform = Platform::get();
253 let animation_clock = AnimationClock::get();
254
255 animated_value.write().prepare(direction);
257
258 let animation_task = spawn(async move {
259 platform.send(UserEvent::RequestRedraw);
260
261 let mut index = 0u128;
262 let mut prev_frame = Instant::now();
263
264 if !peek_has_run_yet {
265 *has_run_yet.write() = true;
266 }
267 is_running.set(true);
268
269 loop {
270 ticker.tick().await;
272
273 platform.send(UserEvent::RequestRedraw);
275
276 let elapsed = animation_clock.correct_elapsed_duration(prev_frame.elapsed());
277
278 index += elapsed.as_millis();
279
280 let is_finished = {
281 let mut animated_value = animated_value.write();
282 let is_finished = animated_value.is_finished(index, direction);
283 animated_value.advance(index, direction);
285
286 is_finished
287 };
288
289 if is_finished {
290 let delay = match on_finish {
291 OnFinish::Reverse { delay } => {
292 direction.toggle();
294 delay
295 }
296 OnFinish::Restart { delay } => delay,
297 OnFinish::Nothing => {
298 break;
300 }
301 };
302
303 if !delay.is_zero() {
304 Timer::after(delay).await;
305 }
306
307 index = 0;
308
309 animated_value.write().prepare(direction);
311 }
312
313 prev_frame = Instant::now();
314 }
315
316 is_running.set(false);
317 task.write().take();
318 });
319
320 task.write().replace(animation_task);
322 }
323}
324pub fn use_animation<Animated: AnimatedValue>(
420 mut run: impl 'static + FnMut(&mut AnimConfiguration) -> Animated,
421) -> UseAnimation<Animated> {
422 use_hook(|| {
423 let mut config = State::create(AnimConfiguration::default());
424 let mut animated_value = State::create(Animated::default());
425 let is_running = State::create(false);
426 let has_run_yet = State::create(false);
427 let task = State::create(None);
428 let last_direction = State::create(AnimDirection::Forward);
429
430 let mut animation = UseAnimation {
431 animated_value,
432 config,
433 is_running,
434 has_run_yet,
435 task,
436 last_direction,
437 };
438
439 Effect::create_sync_with_gen(move |current_gen| {
440 let mut anim_conf = AnimConfiguration::default();
441 animated_value.set(run(&mut anim_conf));
442 *config.write() = anim_conf;
443 match config.peek().on_change {
444 OnChange::Finish if current_gen > 0 => {
445 animation.finish();
446 }
447 OnChange::Rerun if current_gen > 0 => {
448 let last_direction = *animation.last_direction.peek();
449 animation.run(last_direction);
450 }
451 OnChange::Reset if current_gen > 0 => {
452 animation.reset();
453 }
454 _ => {}
455 }
456 });
457
458 match config.peek().on_creation {
459 OnCreation::Run => {
460 animation.run(AnimDirection::Forward);
461 }
462 OnCreation::Finish => {
463 animation.finish();
464 }
465 _ => {}
466 }
467
468 animation
469 })
470}
471
472pub fn use_animation_with_dependencies<Animated: AnimatedValue, D: 'static + Clone + PartialEq>(
489 dependencies: &D,
490 mut run: impl 'static + FnMut(&mut AnimConfiguration, &D) -> Animated,
491) -> UseAnimation<Animated> {
492 let dependencies = use_reactive(dependencies);
493 use_hook(|| {
494 let mut config = State::create(AnimConfiguration::default());
495 let mut animated_value = State::create(Animated::default());
496 let is_running = State::create(false);
497 let has_run_yet = State::create(false);
498 let task = State::create(None);
499 let last_direction = State::create(AnimDirection::Forward);
500
501 let mut animation = UseAnimation {
502 animated_value,
503 config,
504 is_running,
505 has_run_yet,
506 task,
507 last_direction,
508 };
509
510 Effect::create_sync_with_gen(move |current_gen| {
511 let dependencies = dependencies.read();
512 let mut anim_conf = AnimConfiguration::default();
513 animated_value.set(run(&mut anim_conf, &dependencies));
514 *config.write() = anim_conf;
515
516 match config.peek().on_change {
517 OnChange::Finish if current_gen > 0 => {
518 animation.finish();
519 }
520 OnChange::Rerun if current_gen > 0 => {
521 let last_direction = *animation.last_direction.peek();
522 animation.run(last_direction);
523 }
524 OnChange::Reset if current_gen > 0 => {
525 animation.reset();
526 }
527 _ => {}
528 }
529 });
530
531 match config.peek().on_creation {
532 OnCreation::Run => {
533 animation.run(AnimDirection::Forward);
534 }
535 OnCreation::Finish => {
536 animation.finish();
537 }
538 _ => {}
539 }
540
541 animation
542 })
543}
544
545macro_rules! impl_tuple_call {
546 ($(($($type:ident),*)),*) => {
547 $(
548 impl<$($type,)*> AnimatedValue for ($($type,)*)
549 where
550 $($type: AnimatedValue,)*
551 {
552 fn prepare(&mut self, direction: AnimDirection) {
553 #[allow(non_snake_case)]
554 let ($($type,)*) = self;
555 $(
556 $type.prepare(direction);
557 )*
558 }
559
560 fn is_finished(&self, index: u128, direction: AnimDirection) -> bool {
561 #[allow(non_snake_case)]
562 let ($($type,)*) = self;
563 $(
564 if !$type.is_finished(index, direction) {
565 return false;
566 }
567 )*
568 true
569 }
570
571 fn advance(&mut self, index: u128, direction: AnimDirection) {
572 #[allow(non_snake_case)]
573 let ($($type,)*) = self;
574 $(
575 $type.advance(index, direction);
576 )*
577 }
578
579 fn finish(&mut self, direction: AnimDirection) {
580 #[allow(non_snake_case)]
581 let ($($type,)*) = self;
582 $(
583 $type.finish(direction);
584 )*
585 }
586
587 fn into_reversed(self) -> Self {
588 #[allow(non_snake_case)]
589 let ($($type,)*) = self;
590 (
591 $(
592 $type.into_reversed(),
593 )*
594 )
595 }
596 }
597 impl<$($type,)*> ReadAnimatedValue for ($($type,)*)
598 where
599 $($type: ReadAnimatedValue,)*
600 {
601 type Output = (
602 $(
603 <$type as ReadAnimatedValue>::Output,
604 )*
605 );
606 fn value(&self) -> Self::Output {
607 #[allow(non_snake_case)]
608 let ($($type,)*) = self;
609 (
610 $(
611 $type.value(),
612 )*
613 )
614 }
615 }
616 )*
617 };
618}
619
620impl_tuple_call!(
621 (T1),
622 (T1, T2),
623 (T1, T2, T3),
624 (T1, T2, T3, T4),
625 (T1, T2, T3, T4, T5),
626 (T1, T2, T3, T4, T5, T6),
627 (T1, T2, T3, T4, T5, T6, T7),
628 (T1, T2, T3, T4, T5, T6, T7, T8),
629 (T1, T2, T3, T4, T5, T6, T7, T8, T9),
630 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10),
631 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11),
632 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
633);
634
635pub fn use_animation_transition<Animated: AnimatedValue, T: Clone + PartialEq + 'static>(
657 value: impl IntoReadable<T>,
658 mut run: impl 'static + FnMut(T, T) -> Animated,
659) -> UseAnimation<Animated> {
660 let values = use_previous_and_current(value);
661 use_animation(move |conf| {
662 conf.on_change(OnChange::Rerun);
663 conf.on_creation(OnCreation::Run);
664 let (from, to) = values.read().clone();
665 run(from, to)
666 })
667}