1use crate::easing::parse_easing;
4use crate::tween::lock;
5use crate::types::{f32_array, parse_loop_mode};
6use animato_core::{Playable, Update};
7use animato_tween::KeyframeTrack as CoreKeyframeTrack;
8use js_sys::Float32Array;
9use std::sync::{Arc, Mutex};
10use wasm_bindgen::prelude::*;
11
12type Shared<T> = Arc<Mutex<CoreKeyframeTrack<T>>>;
13
14macro_rules! shared_keyframes {
15 ($name:ident, $value_ty:ty) => {
16 #[derive(Clone, Debug)]
17 pub(crate) struct $name {
18 inner: Shared<$value_ty>,
19 }
20
21 impl $name {
22 pub(crate) fn new(inner: Shared<$value_ty>) -> Self {
23 Self { inner }
24 }
25 }
26
27 impl Update for $name {
28 fn update(&mut self, dt: f32) -> bool {
29 lock(&self.inner).update(dt)
30 }
31 }
32
33 impl Playable for $name {
34 fn duration(&self) -> f32 {
35 lock(&self.inner).duration()
36 }
37
38 fn reset(&mut self) {
39 lock(&self.inner).reset();
40 }
41
42 fn seek_to(&mut self, progress: f32) {
43 let mut track = lock(&self.inner);
44 Playable::seek_to(&mut *track, progress);
45 }
46
47 fn is_complete(&self) -> bool {
48 lock(&self.inner).is_complete()
49 }
50
51 fn as_any(&self) -> &dyn core::any::Any {
52 self
53 }
54
55 fn as_any_mut(&mut self) -> &mut dyn core::any::Any {
56 self
57 }
58 }
59 };
60}
61
62shared_keyframes!(SharedKeyframeTrack, f32);
63shared_keyframes!(SharedKeyframeTrack2D, [f32; 2]);
64shared_keyframes!(SharedKeyframeTrack3D, [f32; 3]);
65shared_keyframes!(SharedKeyframeTrack4D, [f32; 4]);
66
67#[wasm_bindgen(js_name = KeyframeTrack)]
69#[derive(Clone, Debug)]
70pub struct KeyframeTrack {
71 inner: Shared<f32>,
72}
73
74#[wasm_bindgen(js_class = KeyframeTrack)]
75impl KeyframeTrack {
76 #[wasm_bindgen(constructor)]
78 pub fn new() -> Self {
79 Self {
80 inner: Arc::new(Mutex::new(CoreKeyframeTrack::new())),
81 }
82 }
83
84 pub fn push(&self, time: f32, value: f32) {
86 let mut track = lock(&self.inner);
87 let next = core::mem::take(&mut *track).push(time, value);
88 *track = next;
89 }
90
91 #[wasm_bindgen(js_name = pushEased)]
93 pub fn push_eased(&self, time: f32, value: f32, easing: &str) -> Result<(), JsValue> {
94 let easing = parse_easing(easing)?;
95 let mut track = lock(&self.inner);
96 let next = core::mem::take(&mut *track).push_eased(time, value, easing);
97 *track = next;
98 Ok(())
99 }
100
101 pub fn update(&self, dt: f32) -> bool {
103 lock(&self.inner).update(dt)
104 }
105
106 pub fn value(&self) -> f32 {
108 lock(&self.inner).value().unwrap_or(f32::NAN)
109 }
110
111 #[wasm_bindgen(js_name = valueAt)]
113 pub fn value_at(&self, seconds: f32) -> f32 {
114 lock(&self.inner).value_at(seconds).unwrap_or(f32::NAN)
115 }
116
117 pub fn duration(&self) -> f32 {
119 lock(&self.inner).duration()
120 }
121
122 pub fn progress(&self) -> f32 {
124 lock(&self.inner).progress()
125 }
126
127 #[wasm_bindgen(js_name = isComplete)]
129 pub fn is_complete(&self) -> bool {
130 lock(&self.inner).is_complete()
131 }
132
133 pub fn reset(&self) {
135 lock(&self.inner).reset();
136 }
137
138 #[wasm_bindgen(js_name = setLoopMode)]
140 pub fn set_loop_mode(&self, mode: &str) -> Result<(), JsValue> {
141 let mut track = lock(&self.inner);
142 let next = core::mem::take(&mut *track).looping(parse_loop_mode(mode)?);
143 *track = next;
144 Ok(())
145 }
146
147 pub(crate) fn shared(&self) -> SharedKeyframeTrack {
148 SharedKeyframeTrack::new(Arc::clone(&self.inner))
149 }
150}
151
152impl Default for KeyframeTrack {
153 fn default() -> Self {
154 Self::new()
155 }
156}
157
158macro_rules! vector_track {
159 (
160 $class:ident,
161 $js_name:ident,
162 $shared:ident,
163 $value_ty:ty,
164 [$($value:ident),+]
165 ) => {
166 #[wasm_bindgen(js_name = $js_name)]
168 #[derive(Clone, Debug)]
169 pub struct $class {
170 inner: Shared<$value_ty>,
171 }
172
173 #[wasm_bindgen(js_class = $js_name)]
174 impl $class {
175 #[wasm_bindgen(constructor)]
177 pub fn new() -> Self {
178 Self {
179 inner: Arc::new(Mutex::new(CoreKeyframeTrack::new())),
180 }
181 }
182
183 pub fn push(&self, time: f32, $($value: f32),+) {
185 let mut track = lock(&self.inner);
186 let next = core::mem::take(&mut *track).push(time, [$($value),+]);
187 *track = next;
188 }
189
190 #[wasm_bindgen(js_name = pushEased)]
192 pub fn push_eased(&self, time: f32, $($value: f32,)+ easing: &str) -> Result<(), JsValue> {
193 let easing = parse_easing(easing)?;
194 let mut track = lock(&self.inner);
195 let next = core::mem::take(&mut *track).push_eased(time, [$($value),+], easing);
196 *track = next;
197 Ok(())
198 }
199
200 pub fn update(&self, dt: f32) -> bool {
202 lock(&self.inner).update(dt)
203 }
204
205 #[wasm_bindgen(js_name = toArray)]
207 pub fn to_array(&self) -> Float32Array {
208 let values = lock(&self.inner).value().unwrap_or_default();
209 f32_array(&values)
210 }
211
212 #[wasm_bindgen(js_name = valueAt)]
214 pub fn value_at(&self, seconds: f32) -> Float32Array {
215 let values = lock(&self.inner).value_at(seconds).unwrap_or_default();
216 f32_array(&values)
217 }
218
219 pub fn duration(&self) -> f32 {
221 lock(&self.inner).duration()
222 }
223
224 pub fn progress(&self) -> f32 {
226 lock(&self.inner).progress()
227 }
228
229 #[wasm_bindgen(js_name = isComplete)]
231 pub fn is_complete(&self) -> bool {
232 lock(&self.inner).is_complete()
233 }
234
235 pub fn reset(&self) {
237 lock(&self.inner).reset();
238 }
239
240 #[wasm_bindgen(js_name = setLoopMode)]
242 pub fn set_loop_mode(&self, mode: &str) -> Result<(), JsValue> {
243 let mut track = lock(&self.inner);
244 let next = core::mem::take(&mut *track).looping(parse_loop_mode(mode)?);
245 *track = next;
246 Ok(())
247 }
248
249 pub(crate) fn shared(&self) -> $shared {
250 $shared::new(Arc::clone(&self.inner))
251 }
252 }
253
254 impl Default for $class {
255 fn default() -> Self {
256 Self::new()
257 }
258 }
259 };
260}
261
262vector_track!(
263 KeyframeTrack2D,
264 KeyframeTrack2D,
265 SharedKeyframeTrack2D,
266 [f32; 2],
267 [x, y]
268);
269vector_track!(
270 KeyframeTrack3D,
271 KeyframeTrack3D,
272 SharedKeyframeTrack3D,
273 [f32; 3],
274 [x, y, z]
275);
276vector_track!(
277 KeyframeTrack4D,
278 KeyframeTrack4D,
279 SharedKeyframeTrack4D,
280 [f32; 4],
281 [x, y, z, w]
282);
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 #[test]
289 fn keyframes_interpolate() {
290 let track = KeyframeTrack::new();
291 track.push(0.0, 0.0);
292 track.push_eased(1.0, 100.0, "linear").unwrap();
293 track.update(0.5);
294 assert_eq!(track.value(), 50.0);
295 }
296}