1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Supporting types for animation coordination
use crate::types::WidgetId;
use super::{Decay, Easing, Spring};
/// Identifies a specific animated property on a specific widget
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct AnimationKey {
pub widget_id: WidgetId,
pub property: String,
}
impl AnimationKey {
pub fn new(widget_id: WidgetId, property: impl Into<String>) -> Self {
Self {
widget_id,
property: property.into(),
}
}
}
/// What drives the animation
#[derive(Debug, Clone)]
pub enum AnimationDriver {
Tween {
from: f64,
to: f64,
start_time: f64,
duration: f64,
easing: Easing,
},
Spring {
spring: Spring,
start_time: f64,
target: f64,
},
Decay {
decay: Decay,
start_time: f64,
initial_value: f64,
},
}
/// Active animation state
#[derive(Debug, Clone)]
pub struct ActiveAnimation {
pub driver: AnimationDriver,
pub current_value: f64,
pub completed: bool,
}
impl ActiveAnimation {
/// Create a new tween animation
pub fn tween(from: f64, to: f64, start_time: f64, duration: f64, easing: Easing) -> Self {
Self {
driver: AnimationDriver::Tween {
from,
to,
start_time,
duration,
easing,
},
current_value: from,
completed: false,
}
}
/// Create a new spring animation
pub fn spring(spring: Spring, start_time: f64, target: f64) -> Self {
Self {
driver: AnimationDriver::Spring {
spring,
start_time,
target,
},
current_value: 1.0, // Spring starts at displacement of 1.0
completed: false,
}
}
/// Create a new decay animation
pub fn decay(decay: Decay, start_time: f64, initial_value: f64) -> Self {
Self {
driver: AnimationDriver::Decay {
decay,
start_time,
initial_value,
},
current_value: initial_value,
completed: false,
}
}
/// Update animation state at the given time
pub fn update(&mut self, time_secs: f64) {
match &self.driver {
AnimationDriver::Tween {
from,
to,
start_time,
duration,
easing,
} => {
let elapsed = time_secs - start_time;
if elapsed >= *duration {
self.current_value = *to;
self.completed = true;
} else {
let t = (elapsed / duration).clamp(0.0, 1.0);
let eased_t = easing.ease(t);
self.current_value = from + (to - from) * eased_t;
self.completed = false;
}
}
AnimationDriver::Spring {
spring,
start_time,
target,
} => {
let elapsed = time_secs - start_time;
let (displacement, _velocity) = spring.evaluate(elapsed);
// Spring returns displacement from target (1.0 at start, 0.0 at rest)
// Convert to actual value: value = target - displacement
self.current_value = target - displacement;
// Check if spring is at rest
self.completed = spring.is_at_rest(elapsed);
}
AnimationDriver::Decay {
decay,
start_time,
initial_value,
} => {
let elapsed = time_secs - start_time;
let (position, _velocity) = decay.evaluate(elapsed);
// Decay returns position offset from start
self.current_value = initial_value + position;
// Check if decay is at rest
self.completed = decay.is_at_rest(elapsed);
}
}
}
}