1use std::cell::RefCell;
2use std::rc::Rc;
3
4use repose_core::{
5 Color, Rect, Size, Vec2,
6 animation::{AnimatedValue, AnimationSpec, KeyframesSpec, RepeatableSpec, SplineKeyframes},
7 animation_driver, remember_state_with_key, request_frame,
8};
9
10pub fn animate_f32_from(
14 key: impl Into<String>,
15 initial: f32,
16 target: f32,
17 spec: AnimationSpec,
18) -> f32 {
19 let key = key.into();
20 let anim_key = format!("anim:f32:{key}");
21 let anim = remember_state_with_key(&anim_key, || AnimatedValue::new(initial, spec));
22 let last = remember_state_with_key(format!("anim:f32_last:{key}"), || f32::NAN);
23
24 animation_driver::touch(&anim_key);
26
27 let mut a = anim.borrow_mut();
28 let mut lt = last.borrow_mut();
29 let should_set_target = lt.is_nan() || (*lt - target).abs() > 1e-6;
30 if should_set_target {
31 a.set_spec(spec);
32 a.set_target(target);
33 *lt = target;
34 drop(lt);
35
36 let reg_key = anim_key;
38 let reg_anim = anim.clone();
39 animation_driver::register(
40 reg_key,
41 Rc::new(RefCell::new(move || reg_anim.borrow_mut().update())),
42 );
43 request_frame();
44
45 *a.get()
46 } else {
47 let needs_reregister = !animation_driver::is_registered(&anim_key) && a.is_animating();
48 let cur = *a.get();
49 drop(lt);
50 drop(a);
51 if needs_reregister {
52 let reg_anim = anim.clone();
53 animation_driver::register(
54 anim_key,
55 Rc::new(RefCell::new(move || reg_anim.borrow_mut().update())),
56 );
57 request_frame();
58 }
59 cur
60 }
61}
62
63pub fn animate_f32(key: impl Into<String>, target: f32, spec: AnimationSpec) -> f32 {
65 animate_f32_from(key, target, target, spec)
66}
67
68macro_rules! animate_from_impl {
69 ($name:ident, $type:ty, $prefix:expr) => {
70 pub fn $name(
71 key: impl Into<String>,
72 initial: $type,
73 target: $type,
74 spec: AnimationSpec,
75 ) -> $type {
76 let key = key.into();
77 let anim_key = format!("anim:{}:{}", $prefix, key);
78 let anim = remember_state_with_key(&anim_key, || AnimatedValue::new(initial, spec));
79 let last =
80 remember_state_with_key(format!("anim:{}_last:{}", $prefix, key), || None::<$type>);
81
82 repose_core::animation_driver::touch(&anim_key);
83
84 let mut a = anim.borrow_mut();
85 let mut lt = last.borrow_mut();
86 if lt.is_none() || lt.as_ref().unwrap() != &target {
87 a.set_spec(spec);
88 a.set_target(target);
89 *lt = Some(target);
90 drop(lt);
91
92 let reg_key = anim_key.clone();
94 let reg_anim = anim.clone();
95 repose_core::animation_driver::register(
96 reg_key,
97 std::rc::Rc::new(std::cell::RefCell::new(move || {
98 reg_anim.borrow_mut().update()
99 })),
100 );
101 repose_core::request_frame();
102
103 *a.get()
104 } else {
105 let needs_reregister =
106 !repose_core::animation_driver::is_registered(&anim_key) && a.is_animating();
107 let cur = *a.get();
108 drop(lt);
109 drop(a);
110 if needs_reregister {
111 let reg_anim = anim.clone();
112 repose_core::animation_driver::register(
113 anim_key,
114 std::rc::Rc::new(std::cell::RefCell::new(move || {
115 reg_anim.borrow_mut().update()
116 })),
117 );
118 repose_core::request_frame();
119 }
120 cur
121 }
122 }
123 };
124}
125
126animate_from_impl!(animate_color_from, Color, "color");
127animate_from_impl!(animate_vec2_from, Vec2, "vec2");
128animate_from_impl!(animate_size_from, Size, "size");
129animate_from_impl!(animate_rect_from, Rect, "rect");
130
131pub fn animate_color(key: impl Into<String>, target: Color, spec: AnimationSpec) -> Color {
133 animate_color_from(key, target, target, spec)
134}
135
136pub fn animate_vec2(key: impl Into<String>, target: Vec2, spec: AnimationSpec) -> Vec2 {
138 animate_vec2_from(key, target, target, spec)
139}
140
141pub fn animate_size(key: impl Into<String>, target: Size, spec: AnimationSpec) -> Size {
143 animate_size_from(key, target, target, spec)
144}
145
146pub fn animate_rect(key: impl Into<String>, target: Rect, spec: AnimationSpec) -> Rect {
148 animate_rect_from(key, target, target, spec)
149}
150
151pub fn animate_keyframes(
166 key: impl Into<String>,
167 keyframes: KeyframesSpec<f32>,
168 spec: AnimationSpec,
169) -> f32 {
170 let key = key.into();
171 let anim_key = format!("anim:kf:{key}");
172 let anim = remember_state_with_key(&anim_key, || AnimatedValue::new(0.0, spec));
173 let last = remember_state_with_key(format!("anim:kf_last:{key}"), || None::<String>);
174 animation_driver::touch(&anim_key);
175
176 let fingerprint = format!("{:?}|{:?}", keyframes.keyframes, spec);
177 let should_restart = {
178 let lt = last.borrow();
179 lt.as_ref() != Some(&fingerprint)
180 };
181 let mut a = anim.borrow_mut();
182 if should_restart {
183 a.set_spec(spec);
184 a.set_keyframes(keyframes);
185 *last.borrow_mut() = Some(fingerprint);
186 drop(a);
187 let reg_anim = anim.clone();
188 animation_driver::register(
189 anim_key.clone(),
190 Rc::new(RefCell::new(move || reg_anim.borrow_mut().update())),
191 );
192 request_frame();
193 *anim.borrow().get()
194 } else {
195 let needs_reregister = !animation_driver::is_registered(&anim_key) && a.is_animating();
196 let cur = *a.get();
197 drop(a);
198 if needs_reregister {
199 let reg_anim = anim.clone();
200 animation_driver::register(
201 anim_key,
202 Rc::new(RefCell::new(move || reg_anim.borrow_mut().update())),
203 );
204 request_frame();
205 }
206 cur
207 }
208}
209
210pub fn animate_spline_keyframes(
225 key: impl Into<String>,
226 keyframes: SplineKeyframes,
227 spec: AnimationSpec,
228) -> f32 {
229 let key = key.into();
230 let anim = remember_state_with_key(format!("anim:spkf_progress:{key}"), || {
232 AnimatedValue::new(0.0, spec)
233 });
234 let spline = remember_state_with_key(format!("anim:spkf:{key}"), || keyframes.clone());
235
236 let anim_key = format!("anim:spkf_progress:{key}");
237 let last = remember_state_with_key(format!("anim:spkf_last:{key}"), || None::<String>);
238 animation_driver::touch(&anim_key);
239 let fingerprint = format!("{}|{spec:?}", keyframes.fingerprint());
240 let should_restart = {
241 let lt = last.borrow();
242 lt.as_ref() != Some(&fingerprint)
243 };
244
245 let progress = {
246 let mut a = anim.borrow_mut();
247 if should_restart {
248 a.set_spec(spec);
249 a.set_target(1.0);
250 *last.borrow_mut() = Some(fingerprint);
251 *spline.borrow_mut() = keyframes;
252 drop(a);
253 let reg_anim = anim.clone();
254 animation_driver::register(
255 anim_key.clone(),
256 Rc::new(RefCell::new(move || reg_anim.borrow_mut().update())),
257 );
258 request_frame();
259 *anim.borrow().get()
260 } else {
261 let needs_reregister = !animation_driver::is_registered(&anim_key) && a.is_animating();
262 let cur = *a.get();
263 drop(a);
264 if needs_reregister {
265 let reg_anim = anim.clone();
266 animation_driver::register(
267 anim_key,
268 Rc::new(RefCell::new(move || reg_anim.borrow_mut().update())),
269 );
270 request_frame();
271 }
272 cur
273 }
274 };
275 spline.borrow().evaluate(progress)
276}
277
278fn with_infinite_repeat(spec: AnimationSpec) -> AnimationSpec {
279 if spec.repeat.is_none() {
280 spec.repeated(RepeatableSpec::infinite().reverse())
281 } else {
282 spec
283 }
284}
285
286pub struct InfiniteTransition {
295 _private: (),
296}
297
298pub fn remember_infinite_transition() -> InfiniteTransition {
311 InfiniteTransition { _private: () }
312}
313
314macro_rules! inf_method {
315 ($method:ident, $from_fn:ident, $type:ty) => {
316 pub fn $method(
317 &self,
318 key: impl Into<String>,
319 initial: $type,
320 target: $type,
321 spec: AnimationSpec,
322 ) -> $type {
323 let key = key.into();
324 $from_fn(
325 format!("inf:{}", key),
326 initial,
327 target,
328 with_infinite_repeat(spec),
329 )
330 }
331 };
332}
333
334impl InfiniteTransition {
335 inf_method!(animate_float, animate_f32_from, f32);
336 inf_method!(animate_color, animate_color_from, Color);
337 inf_method!(animate_vec2, animate_vec2_from, Vec2);
338 inf_method!(animate_size, animate_size_from, Size);
339 inf_method!(animate_rect, animate_rect_from, Rect);
340}
341
342pub struct TransitionScope<T> {
357 key: String,
358 spec: AnimationSpec,
359 state: Rc<RefCell<T>>,
360}
361
362pub fn update_transition<T>(
368 key: impl Into<String>,
369 target_state: T,
370 spec: AnimationSpec,
371) -> TransitionScope<T>
372where
373 T: PartialEq + Clone + 'static,
374{
375 let key = key.into();
376 let state: Rc<RefCell<T>> =
377 remember_state_with_key(format!("tr_state:{key}"), || target_state.clone());
378 if *state.borrow() != target_state {
379 *state.borrow_mut() = target_state;
380 }
381 TransitionScope { key, spec, state }
382}
383
384macro_rules! tr_method {
385 ($method:ident, $fn:ident, $type:ty) => {
386 pub fn $method<F>(&self, child_key: impl Into<String>, map: F) -> $type
387 where
388 F: Fn(&T) -> $type,
389 {
390 let target = map(&*self.state.borrow());
391 $fn(
392 format!("tr:{}:{}", self.key, child_key.into()),
393 target,
394 self.spec,
395 )
396 }
397 };
398}
399
400impl<T> TransitionScope<T> {
401 tr_method!(animate_float, animate_f32, f32);
402 tr_method!(animate_color, animate_color, Color);
403 tr_method!(animate_vec2, animate_vec2, Vec2);
404 tr_method!(animate_size, animate_size, Size);
405 tr_method!(animate_rect, animate_rect, Rect);
406}