Skip to main content

easing_scope/
easing_scope.rs

1use motion_canvas_rs::prelude::*;
2use std::time::Duration;
3
4const CANVAS_WIDTH: u32 = 800;
5const CANVAS_HEIGHT: u32 = 800;
6const START_X: f32 = 250.0;
7const END_X: f32 = 750.0;
8const START_Y: f32 = 100.0;
9const SPACING_Y: f32 = 80.0;
10const ANIM_DURATION_GO: Duration = Duration::from_secs(2);
11const ANIM_DURATION_RETURN: Duration = Duration::from_secs(2);
12const LABEL_FONT: &str = "JetBrains Mono";
13const WAIT_DURATION: Duration = Duration::from_secs(1);
14
15fn main() {
16    let mut project = Project::default()
17        .with_dimensions(CANVAS_WIDTH, CANVAS_HEIGHT)
18        .with_title("Easing Scope")
19        .close_on_finish();
20
21    let easing_configs = vec![
22        ("Linear", easings::linear as fn(f32) -> f32),
23        ("Sine InOut", easings::sine_in_out as fn(f32) -> f32),
24        ("Quad InOut", easings::quad_in_out as fn(f32) -> f32),
25        ("Cubic InOut", easings::cubic_in_out as fn(f32) -> f32),
26        ("Quart InOut", easings::quart_in_out as fn(f32) -> f32),
27        ("Expo InOut", easings::expo_in_out as fn(f32) -> f32),
28        ("Back Out", easings::back_out as fn(f32) -> f32),
29        ("Elastic Out", easings::elastic_out as fn(f32) -> f32),
30        ("Bounce Out", easings::bounce_out as fn(f32) -> f32),
31    ];
32
33    let mut balls = Vec::new();
34
35    let colors = vec![
36        Color::rgb8(0xe1, 0x32, 0x38), // Red
37        Color::rgb8(0x68, 0xab, 0xdf), // Blue
38        Color::rgb8(0xe6, 0xa7, 0x00), // Yellow
39        Color::rgb8(0x20, 0xb2, 0xaa), // Teal
40        Color::rgb8(0x99, 0xc4, 0x7a), // Green
41        Color::rgb8(0xff, 0xc6, 0x6d), // Orange
42        Color::rgb8(0x25, 0xc2, 0x81), // Emerald
43        Color::rgb8(0xf2, 0xf2, 0xf2), // White-ish
44    ];
45
46    for (i, (name, _)) in easing_configs.iter().enumerate() {
47        let y = START_Y + (i as f32 * SPACING_Y);
48        let ball = Circle::default()
49            .with_position(Vec2::new(START_X, y))
50            .with_radius(20.0)
51            .with_fill(colors[i % colors.len()]);
52        let label = TextNode::default()
53            .with_font(LABEL_FONT)
54            .with_position(Vec2::new(START_X - 130.0, y))
55            .with_text(name)
56            .with_font_size(18.0)
57            .with_fill(Color::rgb8(0xcc, 0xcc, 0xcc));
58
59        project.scene.add(Box::new(ball.clone()));
60        project.scene.add(Box::new(label.clone()));
61
62        balls.push(ball);
63    }
64
65    let balls_clone = balls.clone();
66    let configs_clone = easing_configs.clone();
67
68    // Loop animation factory
69    project.scene.video_timeline.add(loop_anim(
70        move || {
71            let balls = &balls_clone;
72            let configs = &configs_clone;
73
74            // 1. Move to right using individual scoped easings
75            let right_anims = all(configs
76                .iter()
77                .enumerate()
78                .map(|(i, (_, easing))| {
79                    let end_pos = Vec2::new(END_X, START_Y + i as f32 * SPACING_Y);
80                    with_easing(
81                        *easing,
82                        vec![balls[i].position.to(end_pos, ANIM_DURATION_GO).into()],
83                    )
84                })
85                .collect());
86
87            // 2. Move back to left using the SAME scoped easings
88            let left_anims = all(configs
89                .iter()
90                .enumerate()
91                .map(|(i, (_, easing))| {
92                    let start_pos = Vec2::new(START_X, START_Y + i as f32 * SPACING_Y);
93                    with_easing(
94                        *easing,
95                        vec![balls[i].position.to(start_pos, ANIM_DURATION_RETURN).into()],
96                    )
97                })
98                .collect());
99
100            chain(vec![
101                right_anims,
102                wait(WAIT_DURATION),
103                left_anims,
104                wait(WAIT_DURATION),
105            ])
106        },
107        None,
108    ));
109
110    project.show().expect("Failed to render");
111}