use crate::style::Style;
use crate::widget::{Color, Rect};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Easing {
#[default]
Linear,
EaseIn,
EaseOut,
EaseInOut,
EaseInCubic,
EaseOutCubic,
EaseInOutCubic,
Bounce,
Step(u8),
}
impl Easing {
pub fn apply(self, t: f32) -> f32 {
let t = t.clamp(0.0, 1.0);
match self {
Easing::Linear => t,
Easing::EaseIn => t * t,
Easing::EaseOut => {
let inv = 1.0 - t;
1.0 - inv * inv
}
Easing::EaseInOut => t * t * (3.0 - 2.0 * t),
Easing::EaseInCubic => t * t * t,
Easing::EaseOutCubic => {
let inv = 1.0 - t;
1.0 - inv * inv * inv
}
Easing::EaseInOutCubic => {
if t < 0.5 {
4.0 * t * t * t
} else {
let inv = -2.0 * t + 2.0;
1.0 - inv * inv * inv / 2.0
}
}
Easing::Bounce => {
let t = 1.0 - t;
let out = if t < 1.0 / 2.75 {
7.5625 * t * t
} else if t < 2.0 / 2.75 {
let t = t - 1.5 / 2.75;
7.5625 * t * t + 0.75
} else if t < 2.5 / 2.75 {
let t = t - 2.25 / 2.75;
7.5625 * t * t + 0.9375
} else {
let t = t - 2.625 / 2.75;
7.5625 * t * t + 0.984375
};
1.0 - out
}
Easing::Step(n) => {
if n == 0 {
t
} else {
let steps = n as f32;
((t * steps) as u32 as f32) / steps
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum LoopMode {
#[default]
Once,
Repeat(u16),
PingPong(u16),
}
fn loop_progress(elapsed: u32, duration_ms: u32, loop_mode: LoopMode) -> (f32, bool) {
if duration_ms == 0 {
return (1.0, true);
}
match loop_mode {
LoopMode::Once => {
let e = if elapsed > duration_ms {
duration_ms
} else {
elapsed
};
(e as f32 / duration_ms as f32, elapsed >= duration_ms)
}
LoopMode::Repeat(n) => {
let cycle = elapsed / duration_ms;
let finished = n > 0 && cycle >= n as u32;
if finished {
(1.0, true)
} else {
let within = elapsed % duration_ms;
(within as f32 / duration_ms as f32, false)
}
}
LoopMode::PingPong(n) => {
let half_cycle = elapsed / duration_ms;
let finished = n > 0 && half_cycle >= (n as u32) * 2;
if finished {
(0.0, true)
} else {
let within = elapsed % duration_ms;
let t = within as f32 / duration_ms as f32;
if half_cycle.is_multiple_of(2) {
(t, false)
} else {
(1.0 - t, false)
}
}
}
}
}
pub struct Fade {
style: *mut Style,
start: Color,
end: Color,
duration_ms: u32,
elapsed: u32,
easing: Easing,
loop_mode: LoopMode,
}
impl Fade {
pub fn new(style: &mut Style, start: Color, end: Color, duration_ms: u32) -> Self {
Self {
style: style as *mut Style,
start,
end,
duration_ms,
elapsed: 0,
easing: Easing::Linear,
loop_mode: LoopMode::Once,
}
}
pub fn with_easing(mut self, easing: Easing) -> Self {
self.easing = easing;
self
}
pub fn with_loop(mut self, loop_mode: LoopMode) -> Self {
self.loop_mode = loop_mode;
self
}
pub fn tick(&mut self, delta_ms: u32) {
self.elapsed = self.elapsed.saturating_add(delta_ms);
let (raw_t, _) = loop_progress(self.elapsed, self.duration_ms, self.loop_mode);
let t = self.easing.apply(raw_t);
let lerp = |a: u8, b: u8| a as f32 + (b as f32 - a as f32) * t;
unsafe {
(*self.style).bg_color = Color(
lerp(self.start.0, self.end.0) as u8,
lerp(self.start.1, self.end.1) as u8,
lerp(self.start.2, self.end.2) as u8,
lerp(self.start.3, self.end.3) as u8,
);
}
}
pub fn finished(&self) -> bool {
let (_, done) = loop_progress(self.elapsed, self.duration_ms, self.loop_mode);
done
}
}
pub struct Slide {
rect: *mut Rect,
start: Rect,
end: Rect,
duration_ms: u32,
elapsed: u32,
easing: Easing,
loop_mode: LoopMode,
}
impl Slide {
pub fn new(rect: &mut Rect, start: Rect, end: Rect, duration_ms: u32) -> Self {
Self {
rect: rect as *mut Rect,
start,
end,
duration_ms,
elapsed: 0,
easing: Easing::Linear,
loop_mode: LoopMode::Once,
}
}
pub fn with_easing(mut self, easing: Easing) -> Self {
self.easing = easing;
self
}
pub fn with_loop(mut self, loop_mode: LoopMode) -> Self {
self.loop_mode = loop_mode;
self
}
pub fn tick(&mut self, delta_ms: u32) {
self.elapsed = self.elapsed.saturating_add(delta_ms);
let (raw_t, _) = loop_progress(self.elapsed, self.duration_ms, self.loop_mode);
let t = self.easing.apply(raw_t);
let lerp = |a: i32, b: i32| a as f32 + (b as f32 - a as f32) * t;
unsafe {
*self.rect = Rect {
x: lerp(self.start.x, self.end.x) as i32,
y: lerp(self.start.y, self.end.y) as i32,
width: lerp(self.start.width, self.end.width) as i32,
height: lerp(self.start.height, self.end.height) as i32,
};
}
}
pub fn finished(&self) -> bool {
let (_, done) = loop_progress(self.elapsed, self.duration_ms, self.loop_mode);
done
}
}
pub struct Motion {
rect: *mut Rect,
start: Rect,
end: Rect,
duration_ms: u32,
elapsed: u32,
easing: Easing,
loop_mode: LoopMode,
}
impl Motion {
pub fn new(rect: &mut Rect, start: Rect, end: Rect, duration_ms: u32) -> Self {
Self {
rect: rect as *mut Rect,
start,
end,
duration_ms,
elapsed: 0,
easing: Easing::Linear,
loop_mode: LoopMode::Once,
}
}
pub fn with_easing(mut self, easing: Easing) -> Self {
self.easing = easing;
self
}
pub fn with_loop(mut self, loop_mode: LoopMode) -> Self {
self.loop_mode = loop_mode;
self
}
pub fn tick(&mut self, delta_ms: u32) {
self.elapsed = self.elapsed.saturating_add(delta_ms);
let (raw_t, _) = loop_progress(self.elapsed, self.duration_ms, self.loop_mode);
let t = self.easing.apply(raw_t);
let lerp = |a: i32, b: i32| a as f32 + (b as f32 - a as f32) * t;
unsafe {
*self.rect = Rect {
x: lerp(self.start.x, self.end.x) as i32,
y: lerp(self.start.y, self.end.y) as i32,
width: lerp(self.start.width, self.end.width) as i32,
height: lerp(self.start.height, self.end.height) as i32,
};
}
}
pub fn finished(&self) -> bool {
let (_, done) = loop_progress(self.elapsed, self.duration_ms, self.loop_mode);
done
}
}
pub struct FadeTransition {
alpha: *mut u8,
start: u8,
end: u8,
duration_ms: u32,
elapsed: u32,
easing: Easing,
loop_mode: LoopMode,
}
impl FadeTransition {
pub fn new(alpha: &mut u8, start: u8, end: u8, duration_ms: u32) -> Self {
Self {
alpha: alpha as *mut u8,
start,
end,
duration_ms,
elapsed: 0,
easing: Easing::Linear,
loop_mode: LoopMode::Once,
}
}
pub fn with_easing(mut self, easing: Easing) -> Self {
self.easing = easing;
self
}
pub fn with_loop(mut self, loop_mode: LoopMode) -> Self {
self.loop_mode = loop_mode;
self
}
pub fn tick(&mut self, delta_ms: u32) {
self.elapsed = self.elapsed.saturating_add(delta_ms);
let (raw_t, _) = loop_progress(self.elapsed, self.duration_ms, self.loop_mode);
let t = self.easing.apply(raw_t);
let v = self.start as f32 + (self.end as f32 - self.start as f32) * t;
unsafe {
*self.alpha = v as u8;
}
}
pub fn finished(&self) -> bool {
let (_, done) = loop_progress(self.elapsed, self.duration_ms, self.loop_mode);
done
}
}
#[derive(Debug, Clone, Copy)]
pub struct AlphaKey {
pub time_ms: u32,
pub alpha: u8,
pub easing: Easing,
}
pub struct KeyFade {
alpha: *mut u8,
keys: alloc::vec::Vec<AlphaKey>,
total_ms: u32,
elapsed: u32,
loop_mode: LoopMode,
}
impl KeyFade {
pub fn new(alpha: &mut u8) -> Self {
Self {
alpha: alpha as *mut u8,
keys: alloc::vec::Vec::new(),
total_ms: 0,
elapsed: 0,
loop_mode: LoopMode::Once,
}
}
pub fn key(mut self, time_ms: u32, alpha: u8, easing: Easing) -> Self {
self.keys.push(AlphaKey {
time_ms,
alpha,
easing,
});
if time_ms > self.total_ms {
self.total_ms = time_ms;
}
self
}
pub fn with_loop(mut self, loop_mode: LoopMode) -> Self {
self.loop_mode = loop_mode;
self
}
pub fn tick(&mut self, delta_ms: u32) {
if self.keys.len() < 2 {
return;
}
self.elapsed = self.elapsed.saturating_add(delta_ms);
let (raw_t, _) = loop_progress(self.elapsed, self.total_ms, self.loop_mode);
let time = raw_t * self.total_ms as f32;
let mut k0 = &self.keys[0];
let mut k1 = &self.keys[1];
for i in 1..self.keys.len() {
if self.keys[i].time_ms as f32 >= time {
k1 = &self.keys[i];
k0 = &self.keys[i - 1];
break;
}
k0 = &self.keys[i - 1];
k1 = &self.keys[i];
}
let seg_dur = k1.time_ms as f32 - k0.time_ms as f32;
let local_t = if seg_dur > 0.0 {
(time - k0.time_ms as f32) / seg_dur
} else {
1.0
};
let eased = k0.easing.apply(local_t);
let v = k0.alpha as f32 + (k1.alpha as f32 - k0.alpha as f32) * eased;
unsafe {
*self.alpha = v as u8;
}
}
pub fn finished(&self) -> bool {
if self.keys.len() < 2 {
return true;
}
let (_, done) = loop_progress(self.elapsed, self.total_ms, self.loop_mode);
done
}
}
pub struct Timeline {
fades: alloc::vec::Vec<Fade>,
slides: alloc::vec::Vec<Slide>,
motions: alloc::vec::Vec<Motion>,
fade_transitions: alloc::vec::Vec<FadeTransition>,
key_fades: alloc::vec::Vec<KeyFade>,
}
impl Timeline {
pub fn new() -> Self {
Self {
fades: alloc::vec::Vec::new(),
slides: alloc::vec::Vec::new(),
motions: alloc::vec::Vec::new(),
fade_transitions: alloc::vec::Vec::new(),
key_fades: alloc::vec::Vec::new(),
}
}
pub fn add_fade(&mut self, fade: Fade) {
self.fades.push(fade);
}
pub fn add_slide(&mut self, slide: Slide) {
self.slides.push(slide);
}
pub fn add_motion(&mut self, motion: Motion) {
self.motions.push(motion);
}
pub fn add_fade_transition(&mut self, ft: FadeTransition) {
self.fade_transitions.push(ft);
}
pub fn add_key_fade(&mut self, kf: KeyFade) {
self.key_fades.push(kf);
}
pub fn tick(&mut self, delta_ms: u32) {
for f in &mut self.fades {
f.tick(delta_ms);
}
for s in &mut self.slides {
s.tick(delta_ms);
}
for m in &mut self.motions {
m.tick(delta_ms);
}
for ft in &mut self.fade_transitions {
ft.tick(delta_ms);
}
for kf in &mut self.key_fades {
kf.tick(delta_ms);
}
self.fades.retain(|f| !f.finished());
self.slides.retain(|s| !s.finished());
self.motions.retain(|m| !m.finished());
self.fade_transitions.retain(|ft| !ft.finished());
self.key_fades.retain(|kf| !kf.finished());
}
pub fn is_empty(&self) -> bool {
self.fades.is_empty()
&& self.slides.is_empty()
&& self.motions.is_empty()
&& self.fade_transitions.is_empty()
&& self.key_fades.is_empty()
}
}
impl Default for Timeline {
fn default() -> Self {
Self::new()
}
}