Skip to main content

nested_cameras/
nested_cameras.rs

1use motion_canvas_rs::prelude::*;
2use std::time::Duration;
3
4fn main() {
5    let mut project = Project::default()
6        .with_title("Nested Cameras Demo")
7        .with_background(Color::rgb8(0x1a, 0x1a, 0x1a))
8        .close_on_finish();
9
10    // Elements to view
11    let rect = Rect::default()
12        .with_size(Vec2::new(200.0, 200.0))
13        .with_fill(Color::BLUE);
14
15    let circle = Circle::default()
16        .with_position(Vec2::new(100.0, 100.0))
17        .with_radius(30.0)
18        .with_fill(Color::WHITE);
19
20    // Inner Camera: Zooms into the action
21    // We disable 'centered' here so it doesn't double-shift the viewport
22    let inner_camera = CameraNode::new(vec![Box::new(rect.clone()), Box::new(circle.clone())])
23        .with_centered(false);
24
25    // Outer Camera: Handles global movement (centered)
26    let outer_camera = CameraNode::new(vec![Box::new(inner_camera.clone())]).with_centered(true);
27
28    // HUD Indicators (Outside cameras to show state)
29    let outer_status = TextNode::default()
30        .with_text("Outer Camera: Panning")
31        .with_position(Vec2::new(20.0, 20.0))
32        .with_anchor(Vec2::new(-1.0, -1.0))
33        .with_font_size(24.0)
34        .with_fill(Color::rgb8(0x2e, 0xcc, 0x71)) // Green
35        .with_opacity(0.0);
36
37    let inner_status = TextNode::default()
38        .with_text("Inner Camera: Zooming")
39        .with_position(Vec2::new(20.0, 60.0))
40        .with_anchor(Vec2::new(-1.0, -1.0))
41        .with_font_size(24.0)
42        .with_fill(Color::rgb8(0xf1, 0xc4, 0x0f)) // Yellow
43        .with_opacity(0.0);
44
45    project.scene.add(Box::new(outer_camera.clone()));
46
47    project.scene.add(Box::new(outer_status.clone()));
48    project.scene.add(Box::new(inner_status.clone()));
49
50    // Animation Flow
51    project.scene.video_timeline.add(loop_anim!(
52        chain![
53            // 1. Outer camera moves left
54            all![
55                outer_status.opacity.to(1.0, Duration::from_millis(300)),
56                outer_camera
57                    .position
58                    .to(Vec2::new(-200.0, 0.0), Duration::from_secs(1)),
59            ],
60            outer_status.opacity.to(0.0, Duration::from_millis(300)),
61            // 2. Inner camera zooms in
62            all![
63                inner_status.opacity.to(1.0, Duration::from_millis(300)),
64                inner_camera.zoom.to(3.0, Duration::from_secs(1)),
65            ],
66            inner_status.opacity.to(0.0, Duration::from_millis(300)),
67            // 3. Reset both
68            all![
69                outer_status.opacity.to(0.5, Duration::from_millis(300)),
70                inner_status.opacity.to(0.5, Duration::from_millis(300)),
71                outer_camera
72                    .position
73                    .to(Vec2::new(0.0, 0.0), Duration::from_secs(1)),
74                inner_camera.zoom.to(1.0, Duration::from_secs(1)),
75            ],
76            all![
77                outer_status.opacity.to(0.0, Duration::from_millis(300)),
78                inner_status.opacity.to(0.0, Duration::from_millis(300)),
79            ],
80        ],
81        None
82    ));
83
84    project.show().expect("Failed to render");
85}