animate_core/mode/
once.rs1use crate::macros::impl_ops;
2use crate::{AnimateState, FRAME_TIME, IS_ANIMATING, Lerp};
3use std::sync::atomic::Ordering;
4
5#[derive(Debug)]
6pub struct Once<T, E, I>(pub(crate) AnimateState<T, E, I>)
7where
8 T: Lerp + PartialEq,
9 E: Fn(f64) -> f64,
10 I: Fn(&T, &T, f64) -> T;
11
12impl<T, E, I> Once<T, E, I>
13where
14 T: Lerp + PartialEq + Default,
15 E: Fn(f64) -> f64,
16 I: Fn(&T, &T, f64) -> T,
17{
18 pub fn new(initial: T, duration: f64, easing: E, interp: I) -> Self {
19 Self(AnimateState::new(initial, duration, easing, interp))
20 }
21
22 pub fn set(&mut self, target: T) {
23 let now = FRAME_TIME.load(Ordering::Relaxed);
24 let inner = self.0.inner.get_mut();
25 inner.start = std::mem::take(&mut inner.current);
26 inner.target = target;
27 inner.started_at = Some(now);
28 }
29
30 pub fn get(&self) -> &T {
31 let now = FRAME_TIME.load(Ordering::Relaxed);
32 unsafe {
33 let inner = &mut *self.0.inner.get();
34 if inner.last_update != now {
35 if let Some(start_t) = inner.started_at {
36 let elapsed = now.saturating_sub(start_t) as f64;
37 let t = (elapsed / self.0.duration).clamp(0.0, 1.0);
38
39 inner.current =
40 (self.0.interp)(&inner.start, &inner.target, (self.0.easing)(t));
41 inner.last_update = now;
42
43 if t >= 1.0 {
44 inner.started_at = None;
45 } else {
46 IS_ANIMATING.store(true, Ordering::Relaxed);
47 }
48 }
49 }
50 &inner.current
51 }
52 }
53
54 pub fn target(&self) -> &T {
55 unsafe {
56 let inner = &*self.0.inner.get();
57 if inner.started_at.is_none() {
58 &inner.current
59 } else {
60 &inner.target
61 }
62 }
63 }
64}
65
66impl_ops!(Once);