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 peek_has_run_yet = *self.has_run_yet.peek();
257
258 let mut ticker = RenderingTicker::get();
259 let platform = Platform::get();
260 let animation_clock = AnimationClock::get();
261
262 animated_value.write().prepare(direction);
264
265 let animation_task = spawn(async move {
266 platform.send(UserEvent::RequestRedraw);
267
268 let mut index = 0u128;
269 let mut prev_frame = Instant::now();
270
271 if !peek_has_run_yet {
272 *has_run_yet.write() = true;
273 }
274 *runs.write() += 1;
275 is_running.set(true);
276
277 loop {
278 ticker.tick().await;
280
281 platform.send(UserEvent::RequestRedraw);
283
284 let elapsed = animation_clock.correct_elapsed_duration(prev_frame.elapsed());
285
286 index += elapsed.as_millis();
287
288 let is_finished = {
289 let mut animated_value = animated_value.write();
290 let is_finished = animated_value.is_finished(index, direction);
291 animated_value.advance(index, direction);
293
294 is_finished
295 };
296
297 if is_finished {
298 let delay = match on_finish {
299 OnFinish::Reverse { delay } => {
300 direction.toggle();
302 delay
303 }
304 OnFinish::Restart { delay } => delay,
305 OnFinish::Nothing => {
306 break;
308 }
309 };
310
311 if !delay.is_zero() {
312 Timer::after(delay).await;
313 }
314
315 index = 0;
316
317 animated_value.write().prepare(direction);
319 }
320
321 prev_frame = Instant::now();
322 }
323
324 is_running.set(false);
325 task.write().take();
326 });
327
328 task.write().replace(animation_task);
330 }
331}
332pub fn use_animation<Animated: AnimatedValue>(
428 mut run: impl 'static + FnMut(&mut AnimConfiguration) -> Animated,
429) -> UseAnimation<Animated> {
430 use_hook(|| {
431 let mut config = State::create(AnimConfiguration::default());
432 let mut animated_value = State::create(Animated::default());
433 let is_running = State::create(false);
434 let has_run_yet = State::create(false);
435 let runs = State::create(0);
436 let task = State::create(None);
437 let last_direction = State::create(AnimDirection::Forward);
438
439 let mut animation = UseAnimation {
440 animated_value,
441 config,
442 is_running,
443 has_run_yet,
444 runs,
445 task,
446 last_direction,
447 };
448
449 Effect::create_sync_with_gen(move |current_gen| {
450 let mut anim_conf = AnimConfiguration::default();
451 animated_value.set(run(&mut anim_conf));
452 *config.write() = anim_conf;
453 match config.peek().on_change {
454 OnChange::Finish if current_gen > 0 => {
455 animation.finish();
456 }
457 OnChange::Rerun if current_gen > 0 => {
458 let last_direction = *animation.last_direction.peek();
459 animation.run(last_direction);
460 }
461 OnChange::Reset if current_gen > 0 => {
462 animation.reset();
463 }
464 _ => {}
465 }
466 });
467
468 match config.peek().on_creation {
469 OnCreation::Run => {
470 animation.run(AnimDirection::Forward);
471 }
472 OnCreation::Finish => {
473 animation.finish();
474 }
475 _ => {}
476 }
477
478 animation
479 })
480}
481
482pub fn use_animation_with_dependencies<Animated: AnimatedValue, D: 'static + Clone + PartialEq>(
499 dependencies: &D,
500 mut run: impl 'static + FnMut(&mut AnimConfiguration, &D) -> Animated,
501) -> UseAnimation<Animated> {
502 let dependencies = use_reactive(dependencies);
503 use_hook(|| {
504 let mut config = State::create(AnimConfiguration::default());
505 let mut animated_value = State::create(Animated::default());
506 let is_running = State::create(false);
507 let has_run_yet = State::create(false);
508 let runs = State::create(0);
509 let task = State::create(None);
510 let last_direction = State::create(AnimDirection::Forward);
511
512 let mut animation = UseAnimation {
513 animated_value,
514 config,
515 is_running,
516 has_run_yet,
517 runs,
518 task,
519 last_direction,
520 };
521
522 Effect::create_sync_with_gen(move |current_gen| {
523 let dependencies = dependencies.read();
524 let mut anim_conf = AnimConfiguration::default();
525 animated_value.set(run(&mut anim_conf, &dependencies));
526 *config.write() = anim_conf;
527
528 match config.peek().on_change {
529 OnChange::Finish if current_gen > 0 => {
530 animation.finish();
531 }
532 OnChange::Rerun if current_gen > 0 => {
533 let last_direction = *animation.last_direction.peek();
534 animation.run(last_direction);
535 }
536 OnChange::Reset if current_gen > 0 => {
537 animation.reset();
538 }
539 _ => {}
540 }
541 });
542
543 match config.peek().on_creation {
544 OnCreation::Run => {
545 animation.run(AnimDirection::Forward);
546 }
547 OnCreation::Finish => {
548 animation.finish();
549 }
550 _ => {}
551 }
552
553 animation
554 })
555}
556
557macro_rules! impl_tuple_call {
558 ($(($($type:ident),*)),*) => {
559 $(
560 impl<$($type,)*> AnimatedValue for ($($type,)*)
561 where
562 $($type: AnimatedValue,)*
563 {
564 fn prepare(&mut self, direction: AnimDirection) {
565 #[allow(non_snake_case)]
566 let ($($type,)*) = self;
567 $(
568 $type.prepare(direction);
569 )*
570 }
571
572 fn is_finished(&self, index: u128, direction: AnimDirection) -> bool {
573 #[allow(non_snake_case)]
574 let ($($type,)*) = self;
575 $(
576 if !$type.is_finished(index, direction) {
577 return false;
578 }
579 )*
580 true
581 }
582
583 fn advance(&mut self, index: u128, direction: AnimDirection) {
584 #[allow(non_snake_case)]
585 let ($($type,)*) = self;
586 $(
587 $type.advance(index, direction);
588 )*
589 }
590
591 fn finish(&mut self, direction: AnimDirection) {
592 #[allow(non_snake_case)]
593 let ($($type,)*) = self;
594 $(
595 $type.finish(direction);
596 )*
597 }
598
599 fn into_reversed(self) -> Self {
600 #[allow(non_snake_case)]
601 let ($($type,)*) = self;
602 (
603 $(
604 $type.into_reversed(),
605 )*
606 )
607 }
608 }
609 impl<$($type,)*> ReadAnimatedValue for ($($type,)*)
610 where
611 $($type: ReadAnimatedValue,)*
612 {
613 type Output = (
614 $(
615 <$type as ReadAnimatedValue>::Output,
616 )*
617 );
618 fn value(&self) -> Self::Output {
619 #[allow(non_snake_case)]
620 let ($($type,)*) = self;
621 (
622 $(
623 $type.value(),
624 )*
625 )
626 }
627 }
628 )*
629 };
630}
631
632impl_tuple_call!(
633 (T1),
634 (T1, T2),
635 (T1, T2, T3),
636 (T1, T2, T3, T4),
637 (T1, T2, T3, T4, T5),
638 (T1, T2, T3, T4, T5, T6),
639 (T1, T2, T3, T4, T5, T6, T7),
640 (T1, T2, T3, T4, T5, T6, T7, T8),
641 (T1, T2, T3, T4, T5, T6, T7, T8, T9),
642 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10),
643 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11),
644 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
645);
646
647pub fn use_animation_transition<Animated: AnimatedValue, T: Clone + PartialEq + 'static>(
669 value: impl IntoReadable<T>,
670 mut run: impl 'static + FnMut(T, T) -> Animated,
671) -> UseAnimation<Animated> {
672 let values = use_previous_and_current(value);
673 use_animation(move |conf| {
674 conf.on_change(OnChange::Rerun);
675 conf.on_creation(OnCreation::Run);
676 let (from, to) = values.read().clone();
677 run(from, to)
678 })
679}