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 has_run_yet(&self) -> State<bool> {
164 self.has_run_yet
165 }
166
167 pub fn run(&self, mut direction: AnimDirection) {
169 let mut is_running = self.is_running;
170 let mut has_run_yet = self.has_run_yet;
171 let mut task = self.task;
172 let mut last_direction = self.last_direction;
173
174 let on_finish = self.config.peek().on_finish;
175 let mut animated_value = self.animated_value;
176
177 last_direction.set(direction);
178
179 if let Some(task) = task.write().take() {
181 task.cancel();
182 }
183
184 let peek_has_run_yet = *self.has_run_yet.peek();
185
186 let mut ticker = RenderingTicker::get();
187 let platform = Platform::get();
188 let animation_clock = AnimationClock::get();
189
190 let animation_task = spawn(async move {
191 platform.send(UserEvent::RequestRedraw);
192
193 let mut index = 0u128;
194 let mut prev_frame = Instant::now();
195
196 animated_value.write().prepare(direction);
198
199 if !peek_has_run_yet {
200 *has_run_yet.write() = true;
201 }
202 is_running.set(true);
203
204 loop {
205 ticker.tick().await;
207
208 platform.send(UserEvent::RequestRedraw);
210
211 let elapsed = animation_clock.correct_elapsed_duration(prev_frame.elapsed());
212
213 index += elapsed.as_millis();
214
215 let mut animated_value = animated_value.write();
216
217 let is_finished = animated_value.is_finished(index, direction);
218
219 animated_value.advance(index, direction);
221
222 prev_frame = Instant::now();
223
224 if is_finished {
225 if OnFinish::Reverse == on_finish {
226 direction.toggle();
228 }
229 match on_finish {
230 OnFinish::Restart | OnFinish::Reverse => {
231 index = 0;
232
233 animated_value.prepare(direction);
235 }
236 OnFinish::Nothing => {
237 break;
239 }
240 }
241 }
242 }
243
244 is_running.set(false);
245 task.write().take();
246 });
247
248 task.write().replace(animation_task);
250 }
251}
252
253pub fn use_animation<Animated: AnimatedValue>(
254 mut run: impl 'static + FnMut(&mut AnimConfiguration) -> Animated,
255) -> UseAnimation<Animated> {
256 use_hook(|| {
257 let mut config = State::create(AnimConfiguration::default());
258 let mut animated_value = State::create(Animated::default());
259 let is_running = State::create(false);
260 let has_run_yet = State::create(false);
261 let task = State::create(None);
262 let last_direction = State::create(AnimDirection::Forward);
263
264 let mut animation = UseAnimation {
265 animated_value,
266 config,
267 is_running,
268 has_run_yet,
269 task,
270 last_direction,
271 };
272
273 Effect::create_sync(move || {
274 let mut anim_conf = AnimConfiguration::default();
275 animated_value.set(run(&mut anim_conf));
276 *config.write() = anim_conf;
277 });
278
279 Effect::create_with_gen(move |current_gen| match config.read().on_change {
280 OnChange::Finish if current_gen > 0 => {
281 animation.finish();
282 }
283 OnChange::Rerun if current_gen > 0 => {
284 let last_direction = *animation.last_direction.peek();
285 animation.run(last_direction);
286 }
287 _ => {}
288 });
289
290 match config.peek().on_creation {
291 OnCreation::Run => {
292 animation.run(AnimDirection::Forward);
293 }
294 OnCreation::Finish => {
295 animation.finish();
296 }
297 _ => {}
298 }
299
300 animation
301 })
302}
303
304pub fn use_animation_with_dependencies<Animated: AnimatedValue, D: 'static + Clone + PartialEq>(
305 dependencies: &D,
306 mut run: impl 'static + FnMut(&mut AnimConfiguration, &D) -> Animated,
307) -> UseAnimation<Animated> {
308 let dependencies = use_reactive(dependencies);
309 use_hook(|| {
310 let mut config = State::create(AnimConfiguration::default());
311 let mut animated_value = State::create(Animated::default());
312 let is_running = State::create(false);
313 let has_run_yet = State::create(false);
314 let task = State::create(None);
315 let last_direction = State::create(AnimDirection::Forward);
316
317 let mut animation = UseAnimation {
318 animated_value,
319 config,
320 is_running,
321 has_run_yet,
322 task,
323 last_direction,
324 };
325
326 Effect::create_sync(move || {
327 let dependencies = dependencies.read();
328 let mut anim_conf = AnimConfiguration::default();
329 animated_value.set(run(&mut anim_conf, &dependencies));
330 *config.write() = anim_conf;
331 });
332
333 Effect::create_with_gen(move |current_gen| match config.read().on_change {
334 OnChange::Finish if current_gen > 0 => {
335 animation.finish();
336 }
337 OnChange::Rerun if current_gen > 0 => {
338 let last_direction = *animation.last_direction.peek();
339 animation.run(last_direction);
340 }
341 _ => {}
342 });
343
344 match config.peek().on_creation {
345 OnCreation::Run => {
346 animation.run(AnimDirection::Forward);
347 }
348 OnCreation::Finish => {
349 animation.finish();
350 }
351 _ => {}
352 }
353
354 animation
355 })
356}
357
358macro_rules! impl_tuple_call {
359 ($(($($type:ident),*)),*) => {
360 $(
361 impl<$($type,)*> AnimatedValue for ($($type,)*)
362 where
363 $($type: AnimatedValue,)*
364 {
365 fn prepare(&mut self, direction: AnimDirection) {
366 #[allow(non_snake_case)]
367 let ($($type,)*) = self;
368 $(
369 $type.prepare(direction);
370 )*
371 }
372
373 fn is_finished(&self, index: u128, direction: AnimDirection) -> bool {
374 #[allow(non_snake_case)]
375 let ($($type,)*) = self;
376 $(
377 if !$type.is_finished(index, direction) {
378 return false;
379 }
380 )*
381 true
382 }
383
384 fn advance(&mut self, index: u128, direction: AnimDirection) {
385 #[allow(non_snake_case)]
386 let ($($type,)*) = self;
387 $(
388 $type.advance(index, direction);
389 )*
390 }
391
392 fn finish(&mut self, direction: AnimDirection) {
393 #[allow(non_snake_case)]
394 let ($($type,)*) = self;
395 $(
396 $type.finish(direction);
397 )*
398 }
399
400 fn into_reversed(self) -> Self {
401 #[allow(non_snake_case)]
402 let ($($type,)*) = self;
403 (
404 $(
405 $type.into_reversed(),
406 )*
407 )
408 }
409 }
410 impl<$($type,)*> ReadAnimatedValue for ($($type,)*)
411 where
412 $($type: ReadAnimatedValue,)*
413 {
414 type Output = (
415 $(
416 <$type as ReadAnimatedValue>::Output,
417 )*
418 );
419 fn value(&self) -> Self::Output {
420 #[allow(non_snake_case)]
421 let ($($type,)*) = self;
422 (
423 $(
424 $type.value(),
425 )*
426 )
427 }
428 }
429 )*
430 };
431}
432
433impl_tuple_call!(
434 (T1),
435 (T1, T2),
436 (T1, T2, T3),
437 (T1, T2, T3, T4),
438 (T1, T2, T3, T4, T5),
439 (T1, T2, T3, T4, T5, T6),
440 (T1, T2, T3, T4, T5, T6, T7),
441 (T1, T2, T3, T4, T5, T6, T7, T8),
442 (T1, T2, T3, T4, T5, T6, T7, T8, T9),
443 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10),
444 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11),
445 (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)
446);