- Implement complete animation system with 20+ motion effects - Add 18+ easing functions (quad, cubic, elastic, back, bounce) - Implement color system with palette and gradient support - Add parser for durations, colors, and CSS gradients - Create comprehensive test suite (14 tests passing) - Add linting and formatting with clippy/rustfmt - Support for figlet integration with custom fonts - Terminal rendering with crossterm - Fix all clippy warnings and lint issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
124 lines
2.6 KiB
Rust
124 lines
2.6 KiB
Rust
use std::time::{Duration, Instant};
|
|
|
|
pub struct Timeline {
|
|
duration_ms: u64,
|
|
fps: u32,
|
|
start_time: Option<Instant>,
|
|
current_frame: usize,
|
|
total_frames: usize,
|
|
}
|
|
|
|
impl Timeline {
|
|
pub fn new(duration_ms: u64, fps: u32) -> Self {
|
|
let total_frames = ((duration_ms as f64 / 1000.0) * fps as f64).ceil() as usize;
|
|
|
|
Self {
|
|
duration_ms,
|
|
fps,
|
|
start_time: None,
|
|
current_frame: 0,
|
|
total_frames,
|
|
}
|
|
}
|
|
|
|
pub fn start(&mut self) {
|
|
self.start_time = Some(Instant::now());
|
|
self.current_frame = 0;
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn reset(&mut self) {
|
|
self.start_time = None;
|
|
self.current_frame = 0;
|
|
}
|
|
|
|
pub fn is_complete(&self) -> bool {
|
|
self.current_frame >= self.total_frames
|
|
}
|
|
|
|
pub fn progress(&self) -> f64 {
|
|
if self.total_frames == 0 {
|
|
return 1.0;
|
|
}
|
|
(self.current_frame as f64 / self.total_frames as f64).min(1.0)
|
|
}
|
|
|
|
pub fn next_frame(&mut self) -> bool {
|
|
if self.is_complete() {
|
|
return false;
|
|
}
|
|
|
|
self.current_frame += 1;
|
|
true
|
|
}
|
|
|
|
pub fn frame_duration(&self) -> Duration {
|
|
Duration::from_millis(1000 / self.fps as u64)
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn elapsed(&self) -> Duration {
|
|
self.start_time
|
|
.map(|start| start.elapsed())
|
|
.unwrap_or(Duration::ZERO)
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn current_frame(&self) -> usize {
|
|
self.current_frame
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn total_frames(&self) -> usize {
|
|
self.total_frames
|
|
}
|
|
|
|
pub fn fps(&self) -> u32 {
|
|
self.fps
|
|
}
|
|
|
|
pub fn duration_ms(&self) -> u64 {
|
|
self.duration_ms
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_timeline_creation() {
|
|
let timeline = Timeline::new(1000, 30);
|
|
assert_eq!(timeline.total_frames(), 30);
|
|
assert_eq!(timeline.fps(), 30);
|
|
}
|
|
|
|
#[test]
|
|
fn test_timeline_progress() {
|
|
let mut timeline = Timeline::new(1000, 10);
|
|
timeline.start();
|
|
|
|
assert_eq!(timeline.progress(), 0.0);
|
|
|
|
for _ in 0..5 {
|
|
timeline.next_frame();
|
|
}
|
|
|
|
assert_eq!(timeline.progress(), 0.5);
|
|
}
|
|
|
|
#[test]
|
|
fn test_timeline_completion() {
|
|
let mut timeline = Timeline::new(1000, 10);
|
|
timeline.start();
|
|
|
|
assert!(!timeline.is_complete());
|
|
|
|
for _ in 0..10 {
|
|
timeline.next_frame();
|
|
}
|
|
|
|
assert!(timeline.is_complete());
|
|
}
|
|
}
|