Skip to main content

aura_anim_iced/behavior/
progress.rs

1use crate::{AnimationHandle, Duration, behavior::TransitionValueKind};
2
3/// Active property transition metadata tracked by a [`PropertyTransition`](crate::PropertyTransition).
4#[derive(Debug, Clone, Copy, PartialEq)]
5pub struct ActivePropertyTransition<K>
6where
7    K: TransitionValueKind,
8    K::Inner: Copy + PartialEq,
9{
10    handle: AnimationHandle,
11    from: K::Inner,
12    to: K::Inner,
13    started_at: Duration,
14    duration: Option<Duration>,
15}
16
17impl<K> ActivePropertyTransition<K>
18where
19    K: TransitionValueKind,
20    K::Inner: Copy + PartialEq,
21{
22    pub(crate) const fn new(
23        handle: AnimationHandle,
24        from: K::Inner,
25        to: K::Inner,
26        started_at: Duration,
27        duration: Option<Duration>,
28    ) -> Self {
29        Self {
30            handle,
31            from,
32            to,
33            started_at,
34            duration,
35        }
36    }
37
38    /// Returns the runtime handle for this transition.
39    #[must_use]
40    pub const fn handle(&self) -> AnimationHandle {
41        self.handle
42    }
43
44    /// Returns the value this transition started from.
45    #[must_use]
46    pub const fn from(&self) -> K::Inner {
47        self.from
48    }
49
50    /// Returns the value this transition targets.
51    #[must_use]
52    pub const fn to(&self) -> K::Inner {
53        self.to
54    }
55
56    /// Returns the runtime timestamp when this transition started.
57    #[must_use]
58    pub const fn started_at(&self) -> Duration {
59        self.started_at
60    }
61
62    /// Returns the finite transition duration, if known.
63    #[must_use]
64    pub const fn duration(&self) -> Option<Duration> {
65        self.duration
66    }
67
68    /// Samples progress for this transition at a runtime timestamp.
69    #[must_use]
70    pub fn progress_at(&self, timestamp: Duration) -> PropertyTransitionProgress<K> {
71        let elapsed = timestamp
72            .checked_sub(self.started_at)
73            .unwrap_or(Duration::ZERO);
74        let progress = self
75            .duration
76            .map(|duration| normalized_progress(elapsed, duration));
77
78        PropertyTransitionProgress {
79            handle: self.handle,
80            from: self.from,
81            to: self.to,
82            elapsed,
83            duration: self.duration,
84            progress,
85        }
86    }
87}
88
89/// Sampled progress for an active property transition.
90#[derive(Debug, Clone, Copy, PartialEq)]
91pub struct PropertyTransitionProgress<K>
92where
93    K: TransitionValueKind,
94    K::Inner: Copy + PartialEq,
95{
96    handle: AnimationHandle,
97    from: K::Inner,
98    to: K::Inner,
99    elapsed: Duration,
100    duration: Option<Duration>,
101    progress: Option<f32>,
102}
103
104impl<K> PropertyTransitionProgress<K>
105where
106    K: TransitionValueKind,
107    K::Inner: Copy + PartialEq,
108{
109    /// Returns the runtime handle for this transition.
110    #[must_use]
111    pub const fn handle(&self) -> AnimationHandle {
112        self.handle
113    }
114
115    /// Returns the value this transition started from.
116    #[must_use]
117    pub const fn from(&self) -> K::Inner {
118        self.from
119    }
120
121    /// Returns the value this transition targets.
122    #[must_use]
123    pub const fn to(&self) -> K::Inner {
124        self.to
125    }
126
127    /// Returns elapsed runtime time since the transition started.
128    #[must_use]
129    pub const fn elapsed(&self) -> Duration {
130        self.elapsed
131    }
132
133    /// Returns the finite transition duration, if known.
134    #[must_use]
135    pub const fn duration(&self) -> Option<Duration> {
136        self.duration
137    }
138
139    /// Returns normalized progress in `[0.0, 1.0]` for finite transitions.
140    #[must_use]
141    pub const fn progress(&self) -> Option<f32> {
142        self.progress
143    }
144}
145
146fn normalized_progress(elapsed: Duration, duration: Duration) -> f32 {
147    let duration_ms = duration.as_millis();
148
149    if duration_ms <= 0.0 {
150        return 1.0;
151    }
152
153    #[allow(
154        clippy::cast_possible_truncation,
155        reason = "Property transition progress is normalized for UI consumption."
156    )]
157    {
158        (elapsed.as_millis() / duration_ms).clamp(0.0, 1.0) as f32
159    }
160}