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 drop(lt);
48 *a.get()
49 }
50}
51
52pub fn animate_f32(key: impl Into<String>, target: f32, spec: AnimationSpec) -> f32 {
54 animate_f32_from(key, target, target, spec)
55}
56
57macro_rules! animate_from_impl {
58 ($name:ident, $type:ty, $prefix:expr) => {
59 pub fn $name(
60 key: impl Into<String>,
61 initial: $type,
62 target: $type,
63 spec: AnimationSpec,
64 ) -> $type {
65 let key = key.into();
66 let anim_key = format!("anim:{}:{}", $prefix, key);
67 let anim = remember_state_with_key(&anim_key, || AnimatedValue::new(initial, spec));
68 let last =
69 remember_state_with_key(format!("anim:{}_last:{}", $prefix, key), || None::<$type>);
70
71 repose_core::animation_driver::touch(&anim_key);
72
73 let mut a = anim.borrow_mut();
74 let mut lt = last.borrow_mut();
75 if lt.is_none() || lt.as_ref().unwrap() != &target {
76 a.set_spec(spec);
77 a.set_target(target);
78 *lt = Some(target);
79 drop(lt);
80
81 let reg_key = anim_key.clone();
83 let reg_anim = anim.clone();
84 repose_core::animation_driver::register(
85 reg_key,
86 std::rc::Rc::new(std::cell::RefCell::new(move || {
87 reg_anim.borrow_mut().update()
88 })),
89 );
90 repose_core::request_frame();
91
92 *a.get()
93 } else {
94 drop(lt);
95 *a.get()
96 }
97 }
98 };
99}
100
101animate_from_impl!(animate_color_from, Color, "color");
102animate_from_impl!(animate_vec2_from, Vec2, "vec2");
103animate_from_impl!(animate_size_from, Size, "size");
104animate_from_impl!(animate_rect_from, Rect, "rect");
105
106pub fn animate_color(key: impl Into<String>, target: Color, spec: AnimationSpec) -> Color {
108 animate_color_from(key, target, target, spec)
109}
110
111pub fn animate_vec2(key: impl Into<String>, target: Vec2, spec: AnimationSpec) -> Vec2 {
113 animate_vec2_from(key, target, target, spec)
114}
115
116pub fn animate_size(key: impl Into<String>, target: Size, spec: AnimationSpec) -> Size {
118 animate_size_from(key, target, target, spec)
119}
120
121pub fn animate_rect(key: impl Into<String>, target: Rect, spec: AnimationSpec) -> Rect {
123 animate_rect_from(key, target, target, spec)
124}
125
126pub fn animate_keyframes(
141 key: impl Into<String>,
142 keyframes: KeyframesSpec<f32>,
143 spec: AnimationSpec,
144) -> f32 {
145 let key = key.into();
146 let anim = remember_state_with_key(format!("anim:kf:{key}"), || AnimatedValue::new(0.0, spec));
147 let mut a = anim.borrow_mut();
148 if !a.has_keyframes() {
149 a.set_keyframes(keyframes);
150 }
151 a.update();
152 *a.get()
153}
154
155pub fn animate_spline_keyframes(
170 key: impl Into<String>,
171 keyframes: SplineKeyframes,
172 spec: AnimationSpec,
173) -> f32 {
174 let key = key.into();
175 let anim = remember_state_with_key(format!("anim:spkf_progress:{key}"), || {
177 AnimatedValue::new(0.0, spec)
178 });
179 let spline = remember_state_with_key(format!("anim:spkf:{key}"), || keyframes);
180
181 let mut a = anim.borrow_mut();
182 let s = spline.borrow();
183
184 a.set_target(1.0);
185 a.update();
186 let progress = *a.get();
187 s.evaluate(progress)
188}
189
190fn with_infinite_repeat(spec: AnimationSpec) -> AnimationSpec {
191 if spec.repeat.is_none() {
192 spec.repeated(RepeatableSpec::infinite().reverse())
193 } else {
194 spec
195 }
196}
197
198pub struct InfiniteTransition {
207 _private: (),
208}
209
210pub fn remember_infinite_transition() -> InfiniteTransition {
223 InfiniteTransition { _private: () }
224}
225
226macro_rules! inf_method {
227 ($method:ident, $from_fn:ident, $type:ty) => {
228 pub fn $method(
229 &self,
230 key: impl Into<String>,
231 initial: $type,
232 target: $type,
233 spec: AnimationSpec,
234 ) -> $type {
235 let key = key.into();
236 $from_fn(
237 format!("inf:{}", key),
238 initial,
239 target,
240 with_infinite_repeat(spec),
241 )
242 }
243 };
244}
245
246impl InfiniteTransition {
247 inf_method!(animate_float, animate_f32_from, f32);
248 inf_method!(animate_color, animate_color_from, Color);
249 inf_method!(animate_vec2, animate_vec2_from, Vec2);
250 inf_method!(animate_size, animate_size_from, Size);
251 inf_method!(animate_rect, animate_rect_from, Rect);
252}
253
254pub struct TransitionScope<T> {
269 key: String,
270 spec: AnimationSpec,
271 state: Rc<RefCell<T>>,
272}
273
274pub fn update_transition<T>(
280 key: impl Into<String>,
281 target_state: T,
282 spec: AnimationSpec,
283) -> TransitionScope<T>
284where
285 T: PartialEq + Clone + 'static,
286{
287 let key = key.into();
288 let state: Rc<RefCell<T>> =
289 remember_state_with_key(format!("tr_state:{key}"), || target_state.clone());
290 if *state.borrow() != target_state {
291 *state.borrow_mut() = target_state;
292 }
293 TransitionScope { key, spec, state }
294}
295
296macro_rules! tr_method {
297 ($method:ident, $fn:ident, $type:ty) => {
298 pub fn $method<F>(&self, child_key: impl Into<String>, map: F) -> $type
299 where
300 F: Fn(&T) -> $type,
301 {
302 let target = map(&*self.state.borrow());
303 $fn(
304 format!("tr:{}:{}", self.key, child_key.into()),
305 target,
306 self.spec,
307 )
308 }
309 };
310}
311
312impl<T> TransitionScope<T> {
313 tr_method!(animate_float, animate_f32, f32);
314 tr_method!(animate_color, animate_color, Color);
315 tr_method!(animate_vec2, animate_vec2, Vec2);
316 tr_method!(animate_size, animate_size, Size);
317 tr_method!(animate_rect, animate_rect, Rect);
318}