use super::{ AnimValue, Transition };
use std::collections::HashMap;
use std::hash::Hash;
use std::time::Duration;
struct Anim {
from: AnimValue,
to: AnimValue,
transition: Transition,
elapsed: Duration,
premultiplied: bool,
}
impl Anim {
fn value_at(&self) -> AnimValue {
let past_delay = self.elapsed.saturating_sub(self.transition.delay);
let t = if self.transition.duration.is_zero() {
1.0
} else {
past_delay.as_secs_f32() / self.transition.duration.as_secs_f32()
};
let eased = self.transition.easing.apply(t);
if self.premultiplied {
self.from.lerp_premultiplied(self.to, eased)
} else {
self.from.lerp(self.to, eased)
}
}
fn finished(&self) -> bool {
self.elapsed >= self.transition.delay + self.transition.duration
}
}
pub struct AnimationManager<K: Eq + Hash + Copy> {
active: HashMap<K, Anim>,
resting: HashMap<K, AnimValue>,
}
impl<K: Eq + Hash + Copy> Default for AnimationManager<K> {
fn default() -> Self {
Self { active: HashMap::new(), resting: HashMap::new() }
}
}
impl<K: Eq + Hash + Copy> AnimationManager<K> {
pub fn new() -> Self {
Self::default()
}
pub fn set_target(&mut self, key: K, target: AnimValue, transition: Option<Transition>) {
self.set_target_impl(key, target, transition, false);
}
pub fn set_color_target(&mut self, key: K, target: AnimValue, transition: Option<Transition>) {
self.set_target_impl(key, target, transition, true);
}
fn set_target_impl(
&mut self,
key: K,
target: AnimValue,
transition: Option<Transition>,
premultiplied: bool
) {
let Some(transition) = transition else {
self.active.remove(&key);
self.resting.insert(key, target);
return;
};
match self.active.get_mut(&key) {
Some(anim) if anim.to == target => {}
Some(anim) => {
anim.from = anim.value_at();
anim.to = target;
anim.transition = transition;
anim.elapsed = Duration::ZERO;
anim.premultiplied = premultiplied;
}
None => {
let from = self.resting.get(&key).copied().unwrap_or(target);
self.resting.insert(key, target);
if from == target {
return;
}
self.active.insert(key, Anim {
from,
to: target,
transition,
elapsed: Duration::ZERO,
premultiplied,
});
}
}
}
pub fn tick(&mut self, dt: Duration) {
for anim in self.active.values_mut() {
anim.elapsed += dt;
}
for (key, anim) in self.active.iter() {
if anim.finished() {
self.resting.insert(*key, anim.to);
}
}
self.active.retain(|_, anim| !anim.finished());
}
pub fn value(&self, key: K) -> Option<AnimValue> {
self.active.get(&key).map(Anim::value_at)
}
pub fn active_keys(&self) -> impl Iterator<Item = &K> {
self.active.keys()
}
pub fn is_animating(&self) -> bool {
!self.active.is_empty()
}
}