use std::cell::RefCell;
use std::rc::Rc;
use repose_core::Color;
use repose_core::{
Rect, Size, Vec2,
animation::{AnimatedValue, AnimationSpec, KeyframesSpec, RepeatableSpec},
remember_state_with_key, request_frame,
};
pub fn animate_f32_from(
key: impl Into<String>,
initial: f32,
target: f32,
spec: AnimationSpec,
) -> f32 {
let key = key.into();
let anim = remember_state_with_key(format!("anim:f32:{key}"), || {
AnimatedValue::new(initial, spec)
});
let last = remember_state_with_key(format!("anim:f32_last:{key}"), || f32::NAN);
let mut a = anim.borrow_mut();
let mut lt = last.borrow_mut();
let should_set_target = lt.is_nan() || (*lt - target).abs() > 1e-6;
if should_set_target {
a.set_spec(spec);
a.set_target(target);
*lt = target;
}
drop(lt);
let still_animating = a.update();
if still_animating {
request_frame();
}
*a.get()
}
pub fn animate_f32(key: impl Into<String>, target: f32, spec: AnimationSpec) -> f32 {
animate_f32_from(key, target, target, spec)
}
pub fn animate_color_from(
key: impl Into<String>,
initial: Color,
target: Color,
spec: AnimationSpec,
) -> Color {
let key = key.into();
let anim = remember_state_with_key(format!("anim:color:{key}"), || {
AnimatedValue::new(initial, spec)
});
let last = remember_state_with_key(format!("anim:color_last:{key}"), || None::<Color>);
let mut a = anim.borrow_mut();
let mut lt = last.borrow_mut();
if lt.is_none() || lt.as_ref().unwrap() != &target {
a.set_spec(spec);
a.set_target(target);
*lt = Some(target);
}
drop(lt);
let still_animating = a.update();
if still_animating {
request_frame();
}
*a.get()
}
pub fn animate_color(key: impl Into<String>, target: Color, spec: AnimationSpec) -> Color {
animate_color_from(key, target, target, spec)
}
pub fn animate_keyframes(
key: impl Into<String>,
keyframes: KeyframesSpec<f32>,
spec: AnimationSpec,
) -> f32 {
let key = key.into();
let anim = remember_state_with_key(format!("anim:kf:{key}"), || AnimatedValue::new(0.0, spec));
let mut a = anim.borrow_mut();
if !a.has_keyframes() {
a.set_keyframes(keyframes);
}
let still = a.update();
if still {
request_frame();
}
*a.get()
}
pub fn animate_vec2_from(
key: impl Into<String>,
initial: Vec2,
target: Vec2,
spec: AnimationSpec,
) -> Vec2 {
let key = key.into();
let anim = remember_state_with_key(format!("anim:vec2:{key}"), || {
AnimatedValue::new(initial, spec)
});
let last = remember_state_with_key(format!("anim:vec2_last:{key}"), || None::<Vec2>);
let mut a = anim.borrow_mut();
let mut lt = last.borrow_mut();
if lt.is_none() || lt.as_ref().unwrap() != &target {
a.set_spec(spec);
a.set_target(target);
*lt = Some(target);
}
drop(lt);
let still_animating = a.update();
if still_animating {
request_frame();
}
*a.get()
}
pub fn animate_vec2(key: impl Into<String>, target: Vec2, spec: AnimationSpec) -> Vec2 {
animate_vec2_from(key, target, target, spec)
}
pub fn animate_size_from(
key: impl Into<String>,
initial: Size,
target: Size,
spec: AnimationSpec,
) -> Size {
let key = key.into();
let anim = remember_state_with_key(format!("anim:size:{key}"), || {
AnimatedValue::new(initial, spec)
});
let last = remember_state_with_key(format!("anim:size_last:{key}"), || None::<Size>);
let mut a = anim.borrow_mut();
let mut lt = last.borrow_mut();
if lt.is_none() || lt.as_ref().unwrap() != &target {
a.set_spec(spec);
a.set_target(target);
*lt = Some(target);
}
drop(lt);
let still_animating = a.update();
if still_animating {
request_frame();
}
*a.get()
}
pub fn animate_size(key: impl Into<String>, target: Size, spec: AnimationSpec) -> Size {
animate_size_from(key, target, target, spec)
}
pub fn animate_rect_from(
key: impl Into<String>,
initial: Rect,
target: Rect,
spec: AnimationSpec,
) -> Rect {
let key = key.into();
let anim = remember_state_with_key(format!("anim:rect:{key}"), || {
AnimatedValue::new(initial, spec)
});
let last = remember_state_with_key(format!("anim:rect_last:{key}"), || None::<Rect>);
let mut a = anim.borrow_mut();
let mut lt = last.borrow_mut();
if lt.is_none() || lt.as_ref().unwrap() != &target {
a.set_spec(spec);
a.set_target(target);
*lt = Some(target);
}
drop(lt);
let still_animating = a.update();
if still_animating {
request_frame();
}
*a.get()
}
pub fn animate_rect(key: impl Into<String>, target: Rect, spec: AnimationSpec) -> Rect {
animate_rect_from(key, target, target, spec)
}
fn with_infinite_repeat(spec: AnimationSpec) -> AnimationSpec {
if spec.repeat.is_none() {
spec.repeated(RepeatableSpec::infinite().reverse())
} else {
spec
}
}
pub struct InfiniteTransition {
_private: (),
}
pub fn remember_infinite_transition() -> InfiniteTransition {
InfiniteTransition { _private: () }
}
impl InfiniteTransition {
pub fn animate_float(
&self,
key: impl Into<String>,
initial: f32,
target: f32,
spec: AnimationSpec,
) -> f32 {
let key = key.into();
animate_f32_from(
format!("inf:{key}"),
initial,
target,
with_infinite_repeat(spec),
)
}
pub fn animate_color(
&self,
key: impl Into<String>,
initial: Color,
target: Color,
spec: AnimationSpec,
) -> Color {
let key = key.into();
animate_color_from(
format!("inf:{key}"),
initial,
target,
with_infinite_repeat(spec),
)
}
pub fn animate_vec2(
&self,
key: impl Into<String>,
initial: Vec2,
target: Vec2,
spec: AnimationSpec,
) -> Vec2 {
let key = key.into();
animate_vec2_from(
format!("inf:{key}"),
initial,
target,
with_infinite_repeat(spec),
)
}
pub fn animate_size(
&self,
key: impl Into<String>,
initial: Size,
target: Size,
spec: AnimationSpec,
) -> Size {
let key = key.into();
animate_size_from(
format!("inf:{key}"),
initial,
target,
with_infinite_repeat(spec),
)
}
pub fn animate_rect(
&self,
key: impl Into<String>,
initial: Rect,
target: Rect,
spec: AnimationSpec,
) -> Rect {
let key = key.into();
animate_rect_from(
format!("inf:{key}"),
initial,
target,
with_infinite_repeat(spec),
)
}
}
pub struct TransitionScope<T> {
key: String,
spec: AnimationSpec,
state: Rc<RefCell<T>>,
}
pub fn update_transition<T>(
key: impl Into<String>,
target_state: T,
spec: AnimationSpec,
) -> TransitionScope<T>
where
T: PartialEq + Clone + 'static,
{
let key = key.into();
let state: Rc<RefCell<T>> =
remember_state_with_key(format!("tr_state:{key}"), || target_state.clone());
if *state.borrow() != target_state {
*state.borrow_mut() = target_state;
}
TransitionScope { key, spec, state }
}
impl<T> TransitionScope<T> {
pub fn animate_float<F>(&self, child_key: impl Into<String>, map: F) -> f32
where
F: Fn(&T) -> f32,
{
let target = map(&*self.state.borrow());
animate_f32(
format!("tr:{}:{}", self.key, child_key.into()),
target,
self.spec,
)
}
pub fn animate_color<F>(&self, child_key: impl Into<String>, map: F) -> Color
where
F: Fn(&T) -> Color,
{
let target = map(&*self.state.borrow());
animate_color(
format!("tr:{}:{}", self.key, child_key.into()),
target,
self.spec,
)
}
pub fn animate_vec2<F>(&self, child_key: impl Into<String>, map: F) -> Vec2
where
F: Fn(&T) -> Vec2,
{
let target = map(&*self.state.borrow());
animate_vec2(
format!("tr:{}:{}", self.key, child_key.into()),
target,
self.spec,
)
}
pub fn animate_size<F>(&self, child_key: impl Into<String>, map: F) -> Size
where
F: Fn(&T) -> Size,
{
let target = map(&*self.state.borrow());
animate_size(
format!("tr:{}:{}", self.key, child_key.into()),
target,
self.spec,
)
}
pub fn animate_rect<F>(&self, child_key: impl Into<String>, map: F) -> Rect
where
F: Fn(&T) -> Rect,
{
let target = map(&*self.state.borrow());
animate_rect(
format!("tr:{}:{}", self.key, child_key.into()),
target,
self.spec,
)
}
}