use super::math::easing::Easing;
pub type EasingFn = fn(f64) -> f64;
fn default_easing_fn(t: f64) -> f64 {
Easing::EaseOutQuad.ease(t)
}
#[derive(Clone, Debug)]
pub struct AnimatedValue {
current: f64,
target: f64,
start_value: f64,
start_time: f64,
duration: f64,
easing: EasingFn,
}
impl AnimatedValue {
pub fn new(initial: f64) -> Self {
Self {
current: initial,
target: initial,
start_value: initial,
start_time: 0.0,
duration: 0.3,
easing: default_easing_fn,
}
}
pub fn with_easing(mut self, easing: EasingFn) -> Self {
self.easing = easing;
self
}
pub fn with_duration(mut self, duration: f64) -> Self {
self.duration = duration;
self
}
pub fn animate_to(&mut self, target: f64, time: f64) {
if (self.target - target).abs() > 0.0001 {
self.start_value = self.current;
self.target = target;
self.start_time = time;
}
}
pub fn update(&mut self, time: f64) -> f64 {
if self.duration <= 0.0 {
self.current = self.target;
return self.current;
}
let elapsed = time - self.start_time;
let t = (elapsed / self.duration).clamp(0.0, 1.0);
let eased_t = (self.easing)(t);
self.current = self.start_value + (self.target - self.start_value) * eased_t;
self.current
}
pub fn is_animating(&self, time: f64) -> bool {
let elapsed = time - self.start_time;
elapsed < self.duration && (self.start_value - self.target).abs() > 0.0001
}
pub fn get(&self) -> f64 {
self.current
}
pub fn target(&self) -> f64 {
self.target
}
pub fn set(&mut self, value: f64) {
self.current = value;
self.target = value;
self.start_value = value;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_animated_value_creation() {
let value = AnimatedValue::new(5.0);
assert_eq!(value.get(), 5.0);
assert_eq!(value.target(), 5.0);
}
#[test]
fn test_animated_value_instant_set() {
let mut value = AnimatedValue::new(0.0);
value.set(10.0);
assert_eq!(value.get(), 10.0);
assert_eq!(value.target(), 10.0);
assert!(!value.is_animating(0.0));
}
#[test]
fn test_animated_value_animation() {
let mut value = AnimatedValue::new(0.0).with_duration(1.0);
value.animate_to(10.0, 0.0);
assert!((value.update(0.0) - 0.0).abs() < 0.1);
let mid = value.update(0.5);
assert!(mid > 3.0 && mid < 10.0);
assert!((value.update(1.0) - 10.0).abs() < 0.1);
assert!(!value.is_animating(1.5));
}
#[test]
fn test_animated_value_zero_duration() {
let mut value = AnimatedValue::new(0.0).with_duration(0.0);
value.animate_to(10.0, 0.0);
assert_eq!(value.update(0.0), 10.0);
}
}