Add Phase 1 high-impact animation effects from Animista
Implements 10 new animation effects: - shake: horizontal vibration with decreasing amplitude - wobble: rotation wobble effect with offset variations - vibrate: rapid small movements in multiple directions - heartbeat: pulsing scale with two-beat rhythm pattern - flip-horizontal: flip text horizontally with character reversal - flip-vertical: flip text vertically with line reversal - swing: pendulum motion with decreasing amplitude - sway: gentle continuous swaying motion - roll-in: roll in from left with rotation effect - roll-out: roll out to right with rotation effect All effects implement the Effect trait and are registered in get_effect() and list_effects() for CLI usage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -393,6 +393,202 @@ impl Effect for GradientFlow {
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 1: High-Impact Effects from Animista
|
||||
|
||||
// Shake effect - horizontal vibration
|
||||
pub struct Shake;
|
||||
impl Effect for Shake {
|
||||
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
|
||||
// Fast oscillation that decreases over time
|
||||
let frequency = 20.0;
|
||||
let amplitude = 10.0 * (1.0 - progress);
|
||||
let offset_x = (progress * frequency * std::f64::consts::PI * 2.0).sin() * amplitude;
|
||||
EffectResult::new(ascii_art.render()).with_offset(offset_x as i32, 0)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"shake"
|
||||
}
|
||||
}
|
||||
|
||||
// Wobble effect - rotation wobble (simulated with offset variations)
|
||||
pub struct Wobble;
|
||||
impl Effect for Wobble {
|
||||
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
|
||||
// Wobble with decreasing amplitude
|
||||
let angle = progress * std::f64::consts::PI * 4.0;
|
||||
let amplitude = 15.0 * (1.0 - progress);
|
||||
let offset_x = (angle.sin() * amplitude) as i32;
|
||||
let offset_y = (angle.cos() * amplitude * 0.3) as i32;
|
||||
EffectResult::new(ascii_art.render()).with_offset(offset_x, offset_y)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"wobble"
|
||||
}
|
||||
}
|
||||
|
||||
// Vibrate effect - rapid small movements
|
||||
pub struct Vibrate;
|
||||
impl Effect for Vibrate {
|
||||
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
|
||||
// Very fast, small vibrations
|
||||
let frequency = 50.0;
|
||||
let amplitude = 3.0;
|
||||
let offset_x = (progress * frequency * std::f64::consts::PI).sin() * amplitude;
|
||||
let offset_y = (progress * frequency * std::f64::consts::PI * 1.3).cos() * amplitude;
|
||||
EffectResult::new(ascii_art.render()).with_offset(offset_x as i32, offset_y as i32)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"vibrate"
|
||||
}
|
||||
}
|
||||
|
||||
// Heartbeat effect - pulsing scale with heartbeat rhythm
|
||||
pub struct Heartbeat;
|
||||
impl Effect for Heartbeat {
|
||||
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
|
||||
// Two-beat pulse pattern like a heartbeat
|
||||
let beat_progress = (progress * 2.0) % 1.0;
|
||||
let scale = if beat_progress < 0.3 {
|
||||
1.0 + (beat_progress / 0.3) * 0.15
|
||||
} else if beat_progress < 0.4 {
|
||||
1.15 - ((beat_progress - 0.3) / 0.1) * 0.15
|
||||
} else if beat_progress < 0.6 {
|
||||
1.0 + ((beat_progress - 0.4) / 0.2) * 0.1
|
||||
} else if beat_progress < 0.7 {
|
||||
1.1 - ((beat_progress - 0.6) / 0.1) * 0.1
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let scaled = ascii_art.scale(scale);
|
||||
EffectResult::new(scaled.render()).with_scale(scale)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"heartbeat"
|
||||
}
|
||||
}
|
||||
|
||||
// Flip horizontal - flip text horizontally
|
||||
pub struct FlipHorizontal;
|
||||
impl Effect for FlipHorizontal {
|
||||
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
|
||||
// Scale horizontally from 1 to -1 (flip)
|
||||
let scale = 1.0 - (progress * 2.0);
|
||||
if scale <= 0.0 {
|
||||
// Show reversed text when flipped
|
||||
let lines: Vec<String> = ascii_art
|
||||
.get_lines()
|
||||
.iter()
|
||||
.map(|line| line.chars().rev().collect())
|
||||
.collect();
|
||||
EffectResult::new(lines.join("\n"))
|
||||
} else {
|
||||
let scaled = ascii_art.scale(scale);
|
||||
EffectResult::new(scaled.render()).with_scale(scale)
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"flip-horizontal"
|
||||
}
|
||||
}
|
||||
|
||||
// Flip vertical - flip text vertically
|
||||
pub struct FlipVertical;
|
||||
impl Effect for FlipVertical {
|
||||
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
|
||||
// Scale vertically with midpoint flip
|
||||
let scale = 1.0 - (progress * 2.0).min(1.0);
|
||||
if progress > 0.5 {
|
||||
// Show reversed lines when flipped
|
||||
let mut lines: Vec<String> = ascii_art
|
||||
.get_lines()
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
lines.reverse();
|
||||
let result_scale = (progress - 0.5) * 2.0;
|
||||
let scaled = AsciiArt::new(lines.join("\n")).scale(result_scale);
|
||||
EffectResult::new(scaled.render()).with_scale(result_scale)
|
||||
} else {
|
||||
let scaled = ascii_art.scale(scale.max(0.1));
|
||||
EffectResult::new(scaled.render()).with_scale(scale.max(0.1))
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"flip-vertical"
|
||||
}
|
||||
}
|
||||
|
||||
// Swing effect - pendulum motion
|
||||
pub struct Swing;
|
||||
impl Effect for Swing {
|
||||
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
|
||||
// Pendulum swing with decreasing amplitude
|
||||
let swings = 2.0;
|
||||
let angle = (progress * swings * std::f64::consts::PI * 2.0).sin() * (1.0 - progress);
|
||||
let offset_x = (angle * 20.0) as i32;
|
||||
let offset_y = (angle.abs() * 5.0) as i32;
|
||||
EffectResult::new(ascii_art.render()).with_offset(offset_x, -offset_y)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"swing"
|
||||
}
|
||||
}
|
||||
|
||||
// Sway effect - gentle swaying motion
|
||||
pub struct Sway;
|
||||
impl Effect for Sway {
|
||||
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
|
||||
// Smooth, gentle sway
|
||||
let angle = (progress * std::f64::consts::PI * 2.0).sin();
|
||||
let offset_x = (angle * 8.0) as i32;
|
||||
let offset_y = (angle.abs() * 2.0) as i32;
|
||||
EffectResult::new(ascii_art.render()).with_offset(offset_x, offset_y)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"sway"
|
||||
}
|
||||
}
|
||||
|
||||
// Roll-in effect - roll in from left with rotation
|
||||
pub struct RollIn;
|
||||
impl Effect for RollIn {
|
||||
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
|
||||
// Slide in from left while appearing to roll
|
||||
let offset_x = ((1.0 - progress) * -(ascii_art.width() as f64 + 20.0)) as i32;
|
||||
let rotation_effect = ((1.0 - progress) * 5.0) as i32;
|
||||
let offset_y = (rotation_effect as f64 * (progress * std::f64::consts::PI).sin()) as i32;
|
||||
EffectResult::new(ascii_art.render()).with_offset(offset_x, offset_y)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"roll-in"
|
||||
}
|
||||
}
|
||||
|
||||
// Roll-out effect - roll out to right with rotation
|
||||
pub struct RollOut;
|
||||
impl Effect for RollOut {
|
||||
fn apply(&self, ascii_art: &AsciiArt, progress: f64) -> EffectResult {
|
||||
// Slide out to right while appearing to roll
|
||||
let offset_x = (progress * (ascii_art.width() as f64 + 20.0)) as i32;
|
||||
let rotation_effect = (progress * 5.0) as i32;
|
||||
let offset_y = (rotation_effect as f64 * (progress * std::f64::consts::PI).sin()) as i32;
|
||||
EffectResult::new(ascii_art.render()).with_offset(offset_x, offset_y)
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"roll-out"
|
||||
}
|
||||
}
|
||||
|
||||
/// Get effect by name
|
||||
pub fn get_effect(name: &str) -> Result<Box<dyn Effect>> {
|
||||
match name {
|
||||
@@ -417,6 +613,16 @@ pub fn get_effect(name: &str) -> Result<Box<dyn Effect>> {
|
||||
"gradient-flow" => Ok(Box::new(GradientFlow)),
|
||||
"rotate-in" => Ok(Box::new(RotateIn)),
|
||||
"rotate-out" => Ok(Box::new(RotateOut)),
|
||||
"shake" => Ok(Box::new(Shake)),
|
||||
"wobble" => Ok(Box::new(Wobble)),
|
||||
"vibrate" => Ok(Box::new(Vibrate)),
|
||||
"heartbeat" => Ok(Box::new(Heartbeat)),
|
||||
"flip-horizontal" => Ok(Box::new(FlipHorizontal)),
|
||||
"flip-vertical" => Ok(Box::new(FlipVertical)),
|
||||
"swing" => Ok(Box::new(Swing)),
|
||||
"sway" => Ok(Box::new(Sway)),
|
||||
"roll-in" => Ok(Box::new(RollIn)),
|
||||
"roll-out" => Ok(Box::new(RollOut)),
|
||||
_ => bail!("Unknown effect: {}", name),
|
||||
}
|
||||
}
|
||||
@@ -446,5 +652,15 @@ pub fn list_effects() -> Vec<&'static str> {
|
||||
"gradient-flow",
|
||||
"rotate-in",
|
||||
"rotate-out",
|
||||
"shake",
|
||||
"wobble",
|
||||
"vibrate",
|
||||
"heartbeat",
|
||||
"flip-horizontal",
|
||||
"flip-vertical",
|
||||
"swing",
|
||||
"sway",
|
||||
"roll-in",
|
||||
"roll-out",
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user