1use std::{
2 ops::Deref,
3 time::Instant,
4};
5
6use freya_core::prelude::*;
7
8#[derive(Default, PartialEq, Clone, Debug)]
9pub struct AnimConfiguration {
10 on_finish: OnFinish,
11 on_creation: OnCreation,
12 on_change: OnChange,
13}
14
15impl AnimConfiguration {
16 pub fn on_finish(&mut self, on_finish: OnFinish) -> &mut Self {
17 self.on_finish = on_finish;
18 self
19 }
20
21 pub fn on_creation(&mut self, on_creation: OnCreation) -> &mut Self {
22 self.on_creation = on_creation;
23 self
24 }
25
26 pub fn on_change(&mut self, on_change: OnChange) -> &mut Self {
27 self.on_change = on_change;
28 self
29 }
30}
31
32#[derive(Clone, Copy, PartialEq)]
34pub enum AnimDirection {
35 Forward,
36 Reverse,
37}
38
39impl AnimDirection {
40 pub fn toggle(&mut self) {
41 match self {
42 Self::Forward => *self = Self::Reverse,
43 Self::Reverse => *self = Self::Forward,
44 }
45 }
46}
47
48#[derive(PartialEq, Clone, Copy, Default, Debug)]
52pub enum OnFinish {
53 #[default]
55 Nothing,
56 Reverse,
58 Restart,
60}
61
62#[derive(PartialEq, Clone, Copy, Default, Debug)]
66pub enum OnCreation {
67 #[default]
69 Nothing,
70 Run,
72 Finish,
74}
75
76#[derive(PartialEq, Clone, Copy, Default, Debug)]
80pub enum OnChange {
81 #[default]
83 Reset,
84 Finish,
86 Rerun,
88}
89
90pub trait ReadAnimatedValue: Clone + 'static {
91 type Output;
92 fn value(&self) -> Self::Output;
93}
94
95pub trait AnimatedValue: Clone + Default + 'static {
96 fn prepare(&mut self, direction: AnimDirection);
97
98 fn is_finished(&self, index: u128, direction: AnimDirection) -> bool;
99
100 fn advance(&mut self, index: u128, direction: AnimDirection);
101
102 fn finish(&mut self, direction: AnimDirection);
103
104 fn into_reversed(self) -> Self;
105}
106
107#[derive(Default, Clone, Copy, PartialEq, Eq)]
108pub enum Ease {
109 In,
110 #[default]
111 Out,
112 InOut,
113}
114
115#[derive(Clone, PartialEq)]
117pub struct UseAnimation<Animated: AnimatedValue> {
118 pub(crate) animated_value: State<Animated>,
119 pub(crate) config: State<AnimConfiguration>,
120 pub(crate) is_running: State<bool>,
121 pub(crate) has_run_yet: State<bool>,
122 pub(crate) task: State<Option<TaskHandle>>,
123 pub(crate) last_direction: State<AnimDirection>,
124}
125impl<T: AnimatedValue> Copy for UseAnimation<T> {}
126
127impl<Animated: AnimatedValue> Deref for UseAnimation<Animated> {
128 type Target = State<Animated>;
129 fn deref(&self) -> &Self::Target {
130 &self.animated_value
131 }
132}
133
134impl<Animated: AnimatedValue> UseAnimation<Animated> {
135 pub fn get(&self) -> ReadRef<'static, Animated> {
137 self.animated_value.read()
138 }
139
140 pub fn start(&mut self) {
142 self.run(AnimDirection::Forward)
143 }
144
145 pub fn reverse(&mut self) {
147 self.run(AnimDirection::Reverse)
148 }
149
150 pub fn finish(&mut self) {
152 if let Some(task) = self.task.write().take() {
153 task.cancel();
154 }
155
156 self.animated_value
157 .write()
158 .finish(*self.last_direction.peek());
159
160 *self.has_run_yet.write() = true;
161 }
162
163 pub fn is_running(&self) -> State<bool> {
164 self.is_running
165 }
166
167 pub fn has_run_yet(&self) -> State<bool> {
168 self.has_run_yet
169 }
170
171 pub fn run(&self, mut direction: AnimDirection) {
173 let mut is_running = self.is_running;
174 let mut has_run_yet = self.has_run_yet;
175 let mut task = self.task;
176 let mut last_direction = self.last_direction;
177
178 let on_finish = self.config.peek().on_finish;
179 let mut animated_value = self.animated_value;
180
181 last_direction.set(direction);
182
183 if let Some(task) = task.write().take() {
185 task.cancel();
186 }
187
188 let peek_has_run_yet = *self.has_run_yet.peek();
189
190 let mut ticker = RenderingTicker::get();
191 let platform = Platform::get();
192 let animation_clock = AnimationClock::get();
193
194 let animation_task = spawn(async move {
195 platform.send(UserEvent::RequestRedraw);
196
197 let mut index = 0u128;
198 let mut prev_frame = Instant::now();
199
200 animated_value.write().prepare(direction);
202
203 if !peek_has_run_yet {
204 *has_run_yet.write() = true;
205 }
206 is_running.set(true);
207
208 loop {
209 ticker.tick().await;
211
212 platform.send(UserEvent::RequestRedraw);
214
215 let elapsed = animation_clock.correct_elapsed_duration(prev_frame.elapsed());
216
217 index += elapsed.as_millis();
218
219 let mut animated_value = animated_value.write();
220
221 let is_finished = animated_value.is_finished(index, direction);
222
223 animated_value.advance(index, direction);
225
226 prev_frame = Instant::now();
227
228 if is_finished {
229 if OnFinish::Reverse == on_finish {
230 direction.toggle();
232 }
233 match on_finish {
234 OnFinish::Restart | OnFinish::Reverse => {
235 index = 0;
236
237 animated_value.prepare(direction);
239 }
240 OnFinish::Nothing => {
241 break;
243 }
244 }
245 }
246 }
247
248 is_running.set(false);
249 task.write().take();
250 });
251
252 task.write().replace(animation_task);
254 }
255}
256
257pub fn use_animation<Animated: AnimatedValue>(
258 mut run: impl 'static + FnMut(&mut AnimConfiguration) -> Animated,
259) -> UseAnimation<Animated> {
260 use_hook(|| {
261 let mut config = State::create(AnimConfiguration::default());
262 let mut animated_value = State::create(Animated::default());
263 let is_running = State::create(false);
264 let has_run_yet = State::create(false);
265 let task = State::create(None);
266 let last_direction = State::create(AnimDirection::Forward);
267
268 let mut animation = UseAnimation {
269 animated_value,
270 config,
271 is_running,
272 has_run_yet,
273 task,
274 last_direction,
275 };
276
277 Effect::create_sync(move || {
278 let mut anim_conf = AnimConfiguration::default();
279 animated_value.set(run(&mut anim_conf));
280 *config.write() = anim_conf;
281 });
282
283 Effect::create_sync_with_gen(move |current_gen| match config.read().on_change {
284 OnChange::Finish if current_gen > 0 => {
285 animation.finish();
286 }
287 OnChange::Rerun if current_gen > 0 => {
288 let last_direction = *animation.last_direction.peek();
289 animation.run(last_direction);
290 }
291 _ => {}
292 });
293
294 match config.peek().on_creation {
295 OnCreation::Run => {
296 animation.run(AnimDirection::Forward);
297 }
298 OnCreation::Finish => {
299 animation.finish();
300 }
301 _ => {}
302 }
303
304 animation
305 })
306}
307
308pub fn use_animation_with_dependencies<Animated: AnimatedValue, D: 'static + Clone + PartialEq>(
309 dependencies: &D,
310 mut run: impl 'static + FnMut(&mut AnimConfiguration, &D) -> Animated,
311) -> UseAnimation<Animated> {
312 let dependencies = use_reactive(dependencies);
313 use_hook(|| {
314 let mut config = State::create(AnimConfiguration::default());
315 let mut animated_value = State::create(Animated::default());
316 let is_running = State::create(false);
317 let has_run_yet = State::create(false);
318 let task = State::create(None);
319 let last_direction = State::create(AnimDirection::Forward);
320
321 let mut animation = UseAnimation {
322 animated_value,
323 config,
324 is_running,
325 has_run_yet,
326 task,
327 last_direction,
328 };
329
330 Effect::create_sync(move || {
331 let dependencies = dependencies.read();
332 let mut anim_conf = AnimConfiguration::default();
333 animated_value.set(run(&mut anim_conf, &dependencies));
334 *config.write() = anim_conf;
335 });
336
337 Effect::create_sync_with_gen(move |current_gen| match config.read().on_change {
338 OnChange::Finish if current_gen > 0 => {
339 animation.finish();
340 }
341 OnChange::Rerun if current_gen > 0 => {
342 let last_direction = *animation.last_direction.peek();
343 animation.run(last_direction);
344 }
345 _ => {}
346 });
347
348 match config.peek().on_creation {
349 OnCreation::Run => {
350 animation.run(AnimDirection::Forward);
351 }
352 OnCreation::Finish => {
353 animation.finish();
354 }
355 _ => {}
356 }
357
358 animation
359 })
360}
361
362macro_rules! impl_tuple_call {
363 ($(($($type:ident),*)),*) => {
364 $(
365 impl<$($type,)*> AnimatedValue for ($($type,)*)
366 where
367 $($type: AnimatedValue,)*
368 {
369 fn prepare(&mut self, direction: AnimDirection) {
370 #[allow(non_snake_case)]
371 let ($($type,)*) = self;
372 $(
373 $type.prepare(direction);
374 )*
375 }
376
377 fn is_finished(&self, index: u128, direction: AnimDirection) -> bool {
378 #[allow(non_snake_case)]
379 let ($($type,)*) = self;
380 $(
381 if !$type.is_finished(index, direction) {
382 return false;
383 }
384 )*
385 true
386 }
387
388 fn advance(&mut self, index: u128, direction: AnimDirection) {
389 #[allow(non_snake_case)]
390 let ($($type,)*) = self;
391 $(
392 $type.advance(index, direction);
393 )*
394 }
395
396 fn finish(&mut self, direction: AnimDirection) {
397 #[allow(non_snake_case)]
398 let ($($type,)*) = self;
399 $(
400 $type.finish(direction);
401 )*
402 }
403
404 fn into_reversed(self) -> Self {
405 #[allow(non_snake_case)]
406 let ($($type,)*) = self;
407 (
408 $(
409 $type.into_reversed(),
410 )*
411 )
412 }
413 }
414 impl<$($type,)*> ReadAnimatedValue for ($($type,)*)
415 where
416 $($type: ReadAnimatedValue,)*
417 {
418 type Output = (
419 $(
420 <$type as ReadAnimatedValue>::Output,
421 )*
422 );
423 fn value(&self) -> Self::Output {
424 #[allow(non_snake_case)]
425 let ($($type,)*) = self;
426 (
427 $(
428 $type.value(),
429 )*
430 )
431 }
432 }
433 )*
434 };
435}
436
437impl_tuple_call!(
438 (T1),
439 (T1, T2),
440 (T1, T2, T3),
441 (T1, T2, T3, T4),
442 (T1, T2, T3, T4, T5),
443 (T1, T2, T3, T4, T5, T6),
444 (T1, T2, T3, T4, T5, T6, T7),
445 (T1, T2, T3, T4, T5, T6, T7, T8),
446 (T1, T2, T3, T4, T5, T6, T7, T8, T9),
447 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10),
448 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11),
449 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
450);