pub struct Project {Show 14 fields
pub width: u32,
pub height: u32,
pub fps: u32,
pub title: String,
pub scene: BaseScene,
pub output_path: PathBuf,
pub use_cache: bool,
pub use_ffmpeg: bool,
pub use_gpu: bool,
pub background_color: Color,
pub close_on_finish: bool,
pub current_time: Duration,
pub paused: bool,
pub speed: f32,
}Fields§
§width: u32§height: u32§fps: u32§title: String§scene: BaseScene§output_path: PathBuf§use_cache: bool§use_ffmpeg: bool§use_gpu: bool§background_color: Color§close_on_finish: bool§current_time: Duration§paused: bool§speed: f32Implementations§
Source§impl Project
impl Project
Sourcepub fn with_fps(self, fps: u32) -> Self
pub fn with_fps(self, fps: u32) -> Self
Examples found in repository?
examples/color_interpolation.rs (line 7)
4fn main() {
5 let mut project = Project::default()
6 .with_dimensions(300, 300)
7 .with_fps(60)
8 .with_title("Color Interpolation")
9 .with_cache(true)
10 .close_on_finish();
11
12 let circle = Circle::default()
13 .with_position(Vec2::new(150.0, 150.0))
14 .with_radius(50.0)
15 .with_fill(Color::RED); // Red
16
17 project.scene.add(Box::new(circle.clone()));
18
19 let duration = Duration::from_secs(1);
20
21 project.scene.video_timeline.add(loop_anim(
22 move || {
23 chain![
24 circle.fill_color.to(Color::YELLOW, duration),
25 circle.fill_color.to(Color::GREEN, duration),
26 circle.fill_color.to(Color::BLUE, duration),
27 circle.fill_color.to(Color::RED, duration),
28 ]
29 },
30 None,
31 ));
32
33 // Show
34 project.show().expect("Failed to render");
35}More examples
examples/math_animation.rs (line 7)
4fn main() {
5 // 1. Initialize Project
6 let mut project = Project::default()
7 .with_fps(60)
8 .with_dimensions(500, 500)
9 .with_title("Math Animation")
10 .close_on_finish();
11
12 // 2. Create MathNode (Typst syntax)
13 let tex = MathNode::default()
14 .with_position(Vec2::new(250.0, 250.0))
15 .with_equation("y = a x^2")
16 .with_font_size(48.0)
17 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2));
18 project.scene.add(Box::new(tex.clone()));
19
20 // 3. Define Animation Sequence
21 project.scene.video_timeline.add(loop_anim(
22 move || {
23 chain![
24 tex.tex("y = a x^2 + b x", Duration::from_secs(1)),
25 tex.tex("e^(i pi) + 1 = 0", Duration::from_secs(1)),
26 tex.tex("y = a x^2", Duration::from_secs(1)),
27 ]
28 },
29 None,
30 ));
31
32 // 4. Run interactive preview
33 project.show().expect("Failed to render");
34}examples/getting_started.rs (line 7)
4fn main() {
5 // 1. Initialize the Project
6 let mut project = Project::default()
7 .with_fps(60)
8 .with_cache(true)
9 .with_title("Getting Started")
10 .close_on_finish();
11
12 // 2. Define Nodes
13 let circle = Circle::default()
14 .with_position(Vec2::new(400.0, 300.0))
15 .with_radius(50.0)
16 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
17
18 let text = TextNode::default()
19 .with_position(Vec2::new(400.0, 450.0))
20 .with_text("Hello Rust")
21 .with_font_size(40.0)
22 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White-ish
23
24 // 3. Add Nodes to the Scene
25 project.scene.add(Box::new(circle.clone()));
26 project.scene.add(Box::new(text.clone()));
27
28 // 4. Add Animations to the Timeline
29 project.scene.video_timeline.add(all![
30 circle.radius.to(100.0, Duration::from_secs(1)),
31 text.position
32 .to(Vec2::new(400.0, 500.0), Duration::from_secs(1)),
33 ]);
34
35 // 5. Show
36 project.show().expect("Failed to render");
37}examples/export.rs (line 7)
4fn main() {
5 // 1. Initialize for Export
6 let mut project = Project::default()
7 .with_fps(30)
8 .with_ffmpeg(true)
9 .with_title("Export")
10 .with_output_path("output")
11 .close_on_finish();
12
13 // 2. Setup Nodes
14 let circle = Circle::default()
15 .with_position(Vec2::new(400.0, 300.0))
16 .with_radius(50.0)
17 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)); // Blue
18
19 let text = TextNode::default()
20 .with_position(Vec2::new(400.0, 50.0))
21 .with_text("Export Demo")
22 .with_font_size(40.0)
23 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White
24
25 project.scene.add(Box::new(circle.clone()));
26 project.scene.add(Box::new(text.clone()));
27
28 // 3. Define Animations (Color and Font Size)
29 project.scene.video_timeline.add(all![
30 // Circle color and size
31 circle
32 .fill_color
33 .to(Color::rgb8(0xf2, 0xf2, 0xf2), Duration::from_secs(2))
34 .ease(easings::quad_in_out),
35 circle
36 .radius
37 .to(150.0, Duration::from_secs(2))
38 .ease(easings::elastic_out),
39 // Text font size
40 text.font_size
41 .to(50.0, Duration::from_secs(2))
42 .ease(easings::cubic_out),
43 ]);
44
45 // 4. Export (Renders frames and combines them into out.mkv)
46 println!("Starting export to {}...", project.output_path.display());
47 project.export().expect("Failed to export");
48}examples/math_code.rs (line 6)
4fn main() {
5 let mut project = Project::default()
6 .with_fps(120)
7 .with_title("Math Code")
8 .close_on_finish();
9
10 let triangle = Polygon::default()
11 .with_position(Vec2::ZERO)
12 .with_anchor(Vec2::new(-1.0, -1.0))
13 .with_points(vec![
14 Vec2::new(0.0, 0.0),
15 Vec2::new(0.0, 200.0),
16 Vec2::new(200.0, 200.0),
17 ])
18 .with_fill(Color::rgb8(0x68, 0xab, 0xdf));
19
20 let lines = vec![
21 Line::default()
22 .with_start(Vec2::new(0.0, 0.0))
23 .with_end(Vec2::new(0.0, 200.0))
24 .with_stroke(Color::WHITE, 10.0),
25 Line::default()
26 .with_start(Vec2::new(0.0, 0.0))
27 .with_end(Vec2::new(200.0, 200.0))
28 .with_stroke(Color::WHITE, 10.0),
29 Line::default()
30 .with_start(Vec2::new(0.0, 200.0))
31 .with_end(Vec2::new(200.0, 200.0))
32 .with_stroke(Color::WHITE, 10.0),
33 ];
34
35 let triangle_line_group = GroupNode::default()
36 .with_nodes(vec![
37 triangle.clone_node(),
38 lines[0].clone_node(),
39 lines[1].clone_node(),
40 lines[2].clone_node(),
41 ])
42 .with_position(Vec2::new(400.0, 200.0))
43 .with_scale(0.0);
44
45 let pytagorean_theorem = MathNode::default()
46 .with_position(Vec2::new(150.0, 150.0))
47 .with_equation("a^2 + b^2 = c^2")
48 .with_font_size(10.0)
49 .with_fill(Color::GRAY)
50 .with_opacity(0.0);
51
52 let text_a = TextNode::default()
53 .with_text("a")
54 .with_fill(Color::GRAY)
55 .with_opacity(0.0)
56 .with_scale(0.7)
57 .with_position(Vec2::new(370.0, 300.0));
58
59 let text_b = TextNode::default()
60 .with_text("b")
61 .with_fill(Color::GRAY)
62 .with_opacity(0.0)
63 .with_scale(0.7)
64 .with_position(Vec2::new(500.0, 430.0));
65
66 let text_c = TextNode::default()
67 .with_text("c")
68 .with_fill(Color::GRAY)
69 .with_opacity(0.0)
70 .with_scale(0.7)
71 .with_position(Vec2::new(510.0, 260.0));
72
73 project.scene.add(Box::new(triangle_line_group.clone()));
74 project.scene.add(Box::new(pytagorean_theorem.clone()));
75 project.scene.add(Box::new(text_a.clone()));
76 project.scene.add(Box::new(text_b.clone()));
77 project.scene.add(Box::new(text_c.clone()));
78
79 project.scene.video_timeline.add(all![
80 all![triangle_line_group
81 .scale
82 .to(Vec2::ONE, Duration::from_secs(1)),],
83 all![
84 pytagorean_theorem.opacity.to(1.0, Duration::from_secs(1)),
85 pytagorean_theorem
86 .font_size
87 .to(32.0, Duration::from_secs(1)),
88 ],
89 sequence![
90 Duration::from_millis(300),
91 text_a.scale.to(Vec2::new(1.0, 1.0), Duration::from_secs(1)),
92 text_b.scale.to(Vec2::new(1.0, 1.0), Duration::from_secs(1)),
93 text_c.scale.to(Vec2::new(1.0, 1.0), Duration::from_secs(1)),
94 text_a.opacity.to(1.0, Duration::from_secs(1)),
95 text_b.opacity.to(1.0, Duration::from_secs(1)),
96 text_c.opacity.to(1.0, Duration::from_secs(1))
97 ]
98 ]);
99
100 project.show().expect("Failed to render");
101}examples/advanced_flow.rs (line 7)
4fn main() {
5 // 1. Initialize Project with full API coverage
6 let mut project = Project::default()
7 .with_fps(120)
8 .with_gpu(true)
9 .with_cache(true)
10 .with_ffmpeg(true)
11 .with_output_path("output")
12 .with_title("Advanced Flow")
13 .close_on_finish();
14
15 // 2. Setup Nodes
16 let mut path = BezPath::new();
17 path.move_to((100.0, 300.0));
18 path.curve_to((250.0, 100.0), (550.0, 500.0), (700.0, 300.0));
19 let path_node = PathNode::default()
20 .with_path(path)
21 .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 2.0);
22 let follower = Circle::default()
23 .with_position(Vec2::new(100.0, 300.0))
24 .with_radius(20.0)
25 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
26
27 // Showcase: Rect and Line
28 let background_rect = Rect::default()
29 .with_position(Vec2::new(400.0, 300.0))
30 .with_size(Vec2::new(760.0, 560.0))
31 .with_fill(Color::rgba8(0x33, 0x33, 0x33, 150))
32 .with_radius(20.0);
33
34 let divider_line = Line::default()
35 .with_start(Vec2::new(0.0, 300.0))
36 .with_end(Vec2::new(0.0, 300.0))
37 .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 1.0);
38 let title_text = TextNode::default()
39 .with_position(Vec2::new(50.0, 50.0))
40 .with_anchor(Vec2::new(-1.0, -1.0))
41 .with_text("Motion Canvas in Rust")
42 .with_font_size(40.0)
43 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
44 .with_font("JetBrains Mono");
45
46 let code_block = CodeNode::default()
47 .with_anchor(Vec2::new(-1.0, -1.0))
48 .with_position(Vec2::new(50.0, 415.0))
49 .with_language("rust")
50 .with_opacity(0.0);
51
52 let math_eq = MathNode::default()
53 .with_position(Vec2::new(150.0, 150.0))
54 .with_equation("f(x) = sin(x)")
55 .with_font_size(30.0)
56 .with_fill(Color::rgb8(0xe6, 0xa7, 0x00)); // Yellow
57
58 let logo = ImageNode::default()
59 .with_position(Vec2::new(650.0, 150.0))
60 .with_path("./examples/images/motion-canvas-logo.png")
61 .with_size(Vec2::new(150.0, 150.0));
62
63 project.scene.video_timeline.add(all![
64 // Show code
65 chain![
66 code_block.opacity.to(1.0, Duration::from_secs(1)),
67 code_block.append("fn main() {\n", Duration::from_secs(1)),
68 code_block.append(
69 " let mut engine = MotionCanvas::new();\n",
70 Duration::from_secs(1)
71 ),
72 code_block.append(" engine.render();\n", Duration::from_secs(1)),
73 code_block.append("}", Duration::from_secs(1)),
74 // Staggered appearance of nodes
75 sequence![
76 Duration::from_millis(200),
77 divider_line
78 .end
79 .to(Vec2::new(800.0, 300.0), Duration::from_secs(1))
80 .ease(easings::cubic_out),
81 follower
82 .radius
83 .to(30.0, Duration::from_millis(500))
84 .ease(easings::elastic_out),
85 ],
86 // The path follow combined with a "race" logic
87 any![
88 follower
89 .position
90 .follow(&path_node, Duration::from_secs(3))
91 .ease(easings::cubic_in_out),
92 // Race: if this 'wait' finishes first, the follow is done
93 wait(Duration::from_secs(4)),
94 ],
95 // Final flourishes using different easings
96 all![
97 follower
98 .radius
99 .to(10.0, Duration::from_secs(1))
100 .ease(easings::quad_out),
101 divider_line
102 .start
103 .to(Vec2::new(400.0, 300.0), Duration::from_secs(1))
104 .ease(easings::cubic_in),
105 ]
106 ]
107 ]);
108
109 // 4. Build Scene
110 project.scene.add(Box::new(background_rect.clone()));
111 project.scene.add(Box::new(divider_line.clone()));
112 project.scene.add(Box::new(path_node.clone()));
113 project.scene.add(Box::new(follower.clone()));
114 project.scene.add(Box::new(title_text.clone()));
115 project.scene.add(Box::new(code_block.clone()));
116 project.scene.add(Box::new(math_eq.clone()));
117 project.scene.add(Box::new(logo.clone()));
118
119 // 5. Run (Choose show() for interactive or export() for PNGs)
120 project.show().expect("Failed to render");
121 // project.export().expect("Failed to export");
122}Additional examples can be found in:
Sourcepub fn with_dimensions(self, width: u32, height: u32) -> Self
pub fn with_dimensions(self, width: u32, height: u32) -> Self
Examples found in repository?
examples/color_interpolation.rs (line 6)
4fn main() {
5 let mut project = Project::default()
6 .with_dimensions(300, 300)
7 .with_fps(60)
8 .with_title("Color Interpolation")
9 .with_cache(true)
10 .close_on_finish();
11
12 let circle = Circle::default()
13 .with_position(Vec2::new(150.0, 150.0))
14 .with_radius(50.0)
15 .with_fill(Color::RED); // Red
16
17 project.scene.add(Box::new(circle.clone()));
18
19 let duration = Duration::from_secs(1);
20
21 project.scene.video_timeline.add(loop_anim(
22 move || {
23 chain![
24 circle.fill_color.to(Color::YELLOW, duration),
25 circle.fill_color.to(Color::GREEN, duration),
26 circle.fill_color.to(Color::BLUE, duration),
27 circle.fill_color.to(Color::RED, duration),
28 ]
29 },
30 None,
31 ));
32
33 // Show
34 project.show().expect("Failed to render");
35}More examples
examples/math_animation.rs (line 8)
4fn main() {
5 // 1. Initialize Project
6 let mut project = Project::default()
7 .with_fps(60)
8 .with_dimensions(500, 500)
9 .with_title("Math Animation")
10 .close_on_finish();
11
12 // 2. Create MathNode (Typst syntax)
13 let tex = MathNode::default()
14 .with_position(Vec2::new(250.0, 250.0))
15 .with_equation("y = a x^2")
16 .with_font_size(48.0)
17 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2));
18 project.scene.add(Box::new(tex.clone()));
19
20 // 3. Define Animation Sequence
21 project.scene.video_timeline.add(loop_anim(
22 move || {
23 chain![
24 tex.tex("y = a x^2 + b x", Duration::from_secs(1)),
25 tex.tex("e^(i pi) + 1 = 0", Duration::from_secs(1)),
26 tex.tex("y = a x^2", Duration::from_secs(1)),
27 ]
28 },
29 None,
30 ));
31
32 // 4. Run interactive preview
33 project.show().expect("Failed to render");
34}examples/audio_demo.rs (line 6)
4fn main() {
5 let mut project = Project::default()
6 .with_dimensions(800, 450)
7 .with_title("Audio Demo")
8 .with_background(Color::rgb8(20, 20, 25))
9 .close_on_finish();
10
11 // Setup Video Timeline
12 let rect = Rect::new(Vec2::new(100.0, 100.0), Vec2::new(200.0, 200.0), Color::RED)
13 .with_anchor(Vec2::new(-1.0, -1.0));
14
15 project.scene.add(Box::new(rect.clone()));
16
17 project.scene.video_timeline.add(chain!(
18 all![
19 rect.position
20 .to(Vec2::new(150.0, 150.0), Duration::from_secs(1)),
21 rect.size.to(Vec2::new(200.0, 50.0), Duration::from_secs(1)),
22 rect.fill_color.to(Color::BLUE, Duration::from_secs(1)),
23 ],
24 all![
25 rect.position
26 .to(Vec2::new(350.0, 150.0), Duration::from_secs(1)),
27 rect.fill_color.to(Color::RED, Duration::from_secs(1)),
28 ]
29 ));
30
31 // Setup Audio Timeline using new macros and builder
32 project.scene.audio_timeline.add(chain!(
33 play!(AudioNode::new("./examples/audios/combo-1.mp3").with_volume(0.5)),
34 audio_wait!(0.05),
35 play!(AudioNode::new("./examples/audios/combo-2.mp3").with_volume(1.0))
36 ));
37
38 println!("Project configured with separate video and audio timelines.");
39 println!(
40 "Video duration: {:?}",
41 project.scene.video_timeline.duration()
42 );
43 println!(
44 "Audio duration: {:?}",
45 project.scene.audio_timeline.duration()
46 );
47
48 project.show().expect("Failed to run audio demo");
49}examples/images.rs (line 6)
4fn main() {
5 let mut project = Project::default()
6 .with_dimensions(600, 600)
7 .with_title("Images")
8 .close_on_finish();
9
10 // Using the sample logo path from the project
11 let png = ImageNode::default()
12 .with_position(Vec2::new(450.0, 450.0))
13 .with_path("./examples/images/motion-canvas-logo.png")
14 .with_size(Vec2::new(200.0, 200.0));
15
16 let svg = ImageNode::default()
17 .with_position(Vec2::new(150.0, 150.0))
18 .with_path("./examples/images/motion-canvas-rs.svg")
19 .with_size(Vec2::new(200.0, 200.0));
20
21 project.scene.add(Box::new(png.clone()));
22 project.scene.add(Box::new(svg.clone()));
23
24 const MOVE_DUR: Duration = Duration::from_secs(2);
25
26 project.scene.video_timeline.add(loop_anim(
27 move || {
28 with_easing(
29 bounce_out,
30 vec![chain![
31 all!(
32 png.position.to(Vec2::new(150.0, 450.0), MOVE_DUR),
33 svg.position.to(Vec2::new(450.0, 150.0), MOVE_DUR),
34 ),
35 all!(
36 png.position.to(Vec2::new(150.0, 150.0), MOVE_DUR),
37 svg.position.to(Vec2::new(450.0, 450.0), MOVE_DUR),
38 ),
39 all!(
40 png.position.to(Vec2::new(450.0, 150.0), MOVE_DUR),
41 svg.position.to(Vec2::new(150.0, 450.0), MOVE_DUR),
42 ),
43 all!(
44 png.position.to(Vec2::new(450.0, 450.0), MOVE_DUR),
45 svg.position.to(Vec2::new(150.0, 150.0), MOVE_DUR),
46 ),
47 ]],
48 )
49 },
50 None,
51 ));
52
53 project.show().expect("Failed to render");
54}examples/grid.rs (line 7)
4fn main() {
5 let mut project = Project::default()
6 .with_title("Grid Example")
7 .with_dimensions(1280, 720)
8 .close_on_finish();
9
10 // 1. Create Grid Node
11 let grid = GridNode::square(Vec2::new(640.0, 360.0), 4.0, 50.0)
12 .with_stroke(Palette::DARK_GRAY, 2.0)
13 .with_opacity(0.0);
14
15 // 2. Create Text Node
16 let text_node = TextNode::default()
17 .with_position(Vec2::new(640.0, 100.0))
18 .with_font_size(36.0)
19 .with_fill(Color::WHITE)
20 .with_text("Grid: 4x4 | Spacing: 50x50");
21
22 // 3. Bind Text to Grid's rows/columns and spacing
23 let cols_sig = grid.columns.clone();
24 let spacing_sig = grid.spacing.clone();
25 let text_link = text_node.text.bind(grid.rows.clone(), move |rows| {
26 let cols = cols_sig.get();
27 let spacing = spacing_sig.get();
28 format!(
29 "Grid: {:.0}x{:.0} | Spacing: {:.0}x{:.0}",
30 cols, rows, spacing.x, spacing.y
31 )
32 });
33
34 // Add to scene
35 project.scene.add(Box::new(grid.clone()));
36 project.scene.add(Box::new(text_node));
37 project.scene.add(Box::new(text_link));
38
39 project.scene.video_timeline.add(chain![
40 grid.opacity
41 .to(1.0, Duration::from_secs(1))
42 .ease(easings::cubic_out),
43 wait(Duration::from_millis(500)),
44 all![
45 grid.rows.to(16.0, Duration::from_secs(2)),
46 grid.columns.to(16.0, Duration::from_secs(2)),
47 grid.stroke_color.to(Palette::BLUE, Duration::from_secs(2)),
48 grid.spacing
49 .to(Vec2::new(100.0, 100.0), Duration::from_secs(2)),
50 ],
51 wait(Duration::from_millis(500)),
52 all![
53 grid.rows.to(8.0, Duration::from_secs(2)),
54 grid.columns.to(8.0, Duration::from_secs(2)),
55 grid.stroke_color
56 .to(Palette::ORANGE, Duration::from_secs(2)),
57 grid.spacing
58 .to(Vec2::new(20.0, 20.0), Duration::from_secs(2)),
59 ],
60 wait(Duration::from_secs(1)),
61 grid.opacity
62 .to(0.0, Duration::from_secs(1))
63 .ease(easings::cubic_out),
64 wait(Duration::from_millis(500)),
65 ]);
66
67 project.show().expect("Failed to render");
68}examples/shapes.rs (line 5)
3fn main() {
4 let mut project = Project::default()
5 .with_dimensions(800, 300)
6 .with_title("Shapes");
7
8 // Configuration
9 let y_shapes = 150.0;
10 let y_labels = 50.0;
11 let spacing = 180.0;
12 let x_start = 130.0;
13
14 // Circle
15 let circle_text = TextNode::default()
16 .with_text("Circle")
17 .with_position(Vec2::new(x_start, y_labels))
18 .with_anchor(Vec2::ZERO)
19 .with_font_size(24.0)
20 .with_fill(Color::DIM_GRAY);
21 let circle = Circle::default()
22 .with_position(Vec2::new(x_start, y_shapes))
23 .with_radius(50.0)
24 .with_fill(Color::rgb8(0xe1, 0x32, 0x38));
25
26 // Rectangle
27 let x_rect = x_start + spacing;
28 let rect_text = TextNode::default()
29 .with_text("Rect")
30 .with_position(Vec2::new(x_rect, y_labels))
31 .with_anchor(Vec2::ZERO)
32 .with_font_size(24.0)
33 .with_fill(Color::DIM_GRAY);
34 let rect = Rect::default()
35 .with_position(Vec2::new(x_rect, y_shapes))
36 .with_size(Vec2::new(100.0, 100.0))
37 .with_radius(8.0)
38 .with_fill(Color::rgb8(0x68, 0xab, 0xdf));
39
40 // Line
41 let x_line = x_rect + spacing;
42 let line_text = TextNode::default()
43 .with_text("Line")
44 .with_position(Vec2::new(x_line, y_labels))
45 .with_anchor(Vec2::ZERO)
46 .with_font_size(24.0)
47 .with_fill(Color::DIM_GRAY);
48 let line = Line::default()
49 .with_position(Vec2::new(x_line, y_shapes))
50 .with_start(Vec2::new(-50.0, 50.0))
51 .with_end(Vec2::new(50.0, -50.0))
52 .with_stroke(Color::WHITE, 4.0);
53
54 // Polygon
55 let x_poly = x_line + spacing;
56 let poly_text = TextNode::default()
57 .with_text("Poly")
58 .with_position(Vec2::new(x_poly, y_labels))
59 .with_anchor(Vec2::ZERO)
60 .with_font_size(24.0)
61 .with_fill(Color::DIM_GRAY);
62 let poly = Polygon::regular(5, 50.0)
63 .with_position(Vec2::new(x_poly, y_shapes))
64 .with_fill(Color::rgb8(0xe6, 0xa7, 0x00));
65
66 // Add all nodes to the scene
67 project.scene.add(Box::new(circle));
68 project.scene.add(Box::new(circle_text));
69 project.scene.add(Box::new(rect));
70 project.scene.add(Box::new(rect_text));
71 project.scene.add(Box::new(line));
72 project.scene.add(Box::new(line_text));
73 project.scene.add(Box::new(poly));
74 project.scene.add(Box::new(poly_text));
75
76 project.show().expect("Failed to render");
77}Additional examples can be found in:
Sourcepub fn with_title(self, title: &str) -> Self
pub fn with_title(self, title: &str) -> Self
Examples found in repository?
examples/color_interpolation.rs (line 8)
4fn main() {
5 let mut project = Project::default()
6 .with_dimensions(300, 300)
7 .with_fps(60)
8 .with_title("Color Interpolation")
9 .with_cache(true)
10 .close_on_finish();
11
12 let circle = Circle::default()
13 .with_position(Vec2::new(150.0, 150.0))
14 .with_radius(50.0)
15 .with_fill(Color::RED); // Red
16
17 project.scene.add(Box::new(circle.clone()));
18
19 let duration = Duration::from_secs(1);
20
21 project.scene.video_timeline.add(loop_anim(
22 move || {
23 chain![
24 circle.fill_color.to(Color::YELLOW, duration),
25 circle.fill_color.to(Color::GREEN, duration),
26 circle.fill_color.to(Color::BLUE, duration),
27 circle.fill_color.to(Color::RED, duration),
28 ]
29 },
30 None,
31 ));
32
33 // Show
34 project.show().expect("Failed to render");
35}More examples
examples/math_animation.rs (line 9)
4fn main() {
5 // 1. Initialize Project
6 let mut project = Project::default()
7 .with_fps(60)
8 .with_dimensions(500, 500)
9 .with_title("Math Animation")
10 .close_on_finish();
11
12 // 2. Create MathNode (Typst syntax)
13 let tex = MathNode::default()
14 .with_position(Vec2::new(250.0, 250.0))
15 .with_equation("y = a x^2")
16 .with_font_size(48.0)
17 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2));
18 project.scene.add(Box::new(tex.clone()));
19
20 // 3. Define Animation Sequence
21 project.scene.video_timeline.add(loop_anim(
22 move || {
23 chain![
24 tex.tex("y = a x^2 + b x", Duration::from_secs(1)),
25 tex.tex("e^(i pi) + 1 = 0", Duration::from_secs(1)),
26 tex.tex("y = a x^2", Duration::from_secs(1)),
27 ]
28 },
29 None,
30 ));
31
32 // 4. Run interactive preview
33 project.show().expect("Failed to render");
34}examples/getting_started.rs (line 9)
4fn main() {
5 // 1. Initialize the Project
6 let mut project = Project::default()
7 .with_fps(60)
8 .with_cache(true)
9 .with_title("Getting Started")
10 .close_on_finish();
11
12 // 2. Define Nodes
13 let circle = Circle::default()
14 .with_position(Vec2::new(400.0, 300.0))
15 .with_radius(50.0)
16 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
17
18 let text = TextNode::default()
19 .with_position(Vec2::new(400.0, 450.0))
20 .with_text("Hello Rust")
21 .with_font_size(40.0)
22 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White-ish
23
24 // 3. Add Nodes to the Scene
25 project.scene.add(Box::new(circle.clone()));
26 project.scene.add(Box::new(text.clone()));
27
28 // 4. Add Animations to the Timeline
29 project.scene.video_timeline.add(all![
30 circle.radius.to(100.0, Duration::from_secs(1)),
31 text.position
32 .to(Vec2::new(400.0, 500.0), Duration::from_secs(1)),
33 ]);
34
35 // 5. Show
36 project.show().expect("Failed to render");
37}examples/code_advanced.rs (line 6)
4fn main() {
5 let mut project = Project::default()
6 .with_title("Code Advanced")
7 .close_on_finish();
8
9 let code = CodeNode::default()
10 .with_position(Vec2::new(50.0, 50.0))
11 .with_code(
12 r#"fn main() {
13 println!("Hello");
14}"#,
15 )
16 .with_language("rust")
17 .with_font_size(32.0)
18 .with_dim_opacity(0.1);
19
20 project.scene.add(Box::new(code.clone()));
21
22 project.scene.video_timeline.add(sequence![
23 Duration::from_secs(1),
24 // 1. Select line 2 (println) - using 1-based index string
25 code.select_string("2", Duration::from_millis(300)),
26 // 2. Select range 1-2
27 code.select_string("1-2", Duration::from_millis(300)),
28 // 3. Append a comment
29 code.append("\n// Done!", Duration::from_millis(300)),
30 // 3. Reset selection
31 code.select_lines(vec![], Duration::from_millis(300)),
32 // 4. Prepend a header (Now natively lazy, no wrapper needed!)
33 code.prepend("// My Script\n", Duration::from_millis(300)),
34 ]);
35
36 project.show().expect("Failed to render");
37}examples/export.rs (line 9)
4fn main() {
5 // 1. Initialize for Export
6 let mut project = Project::default()
7 .with_fps(30)
8 .with_ffmpeg(true)
9 .with_title("Export")
10 .with_output_path("output")
11 .close_on_finish();
12
13 // 2. Setup Nodes
14 let circle = Circle::default()
15 .with_position(Vec2::new(400.0, 300.0))
16 .with_radius(50.0)
17 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)); // Blue
18
19 let text = TextNode::default()
20 .with_position(Vec2::new(400.0, 50.0))
21 .with_text("Export Demo")
22 .with_font_size(40.0)
23 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White
24
25 project.scene.add(Box::new(circle.clone()));
26 project.scene.add(Box::new(text.clone()));
27
28 // 3. Define Animations (Color and Font Size)
29 project.scene.video_timeline.add(all![
30 // Circle color and size
31 circle
32 .fill_color
33 .to(Color::rgb8(0xf2, 0xf2, 0xf2), Duration::from_secs(2))
34 .ease(easings::quad_in_out),
35 circle
36 .radius
37 .to(150.0, Duration::from_secs(2))
38 .ease(easings::elastic_out),
39 // Text font size
40 text.font_size
41 .to(50.0, Duration::from_secs(2))
42 .ease(easings::cubic_out),
43 ]);
44
45 // 4. Export (Renders frames and combines them into out.mkv)
46 println!("Starting export to {}...", project.output_path.display());
47 project.export().expect("Failed to export");
48}examples/polygon.rs (line 5)
4fn main() {
5 let mut project = Project::default().with_title("Polygon").close_on_finish();
6
7 // Create a regular pentagon
8 let pentagon = Polygon::regular(5, 100.0)
9 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)) // Red
10 .with_position(Vec2::new(200.0, 300.0))
11 .with_scale(0.0);
12
13 // Create a custom triangle
14 let triangle = Polygon::default()
15 .with_position(Vec2::new(500.0, 300.0))
16 .with_points(vec![
17 Vec2::new(0.0, -100.0),
18 Vec2::new(100.0, 100.0),
19 Vec2::new(-100.0, 100.0),
20 ])
21 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
22 .with_stroke(Color::WHITE, 4.0);
23
24 project.scene.add(Box::new(pentagon.clone()));
25 project.scene.add(Box::new(triangle.clone()));
26
27 // Animate rotation and opacity
28 project.scene.video_timeline.add(all![
29 chain![
30 pentagon.scale.to(Vec2::ONE, Duration::from_secs(1)),
31 pentagon
32 .rotation
33 .to(std::f32::consts::PI, Duration::from_secs(2)),
34 pentagon.scale.to(-Vec2::ONE, Duration::from_secs(1)),
35 ],
36 chain![
37 triangle.opacity.to(1.0, Duration::from_secs(1)),
38 triangle
39 .position
40 .to(Vec2::new(500.0, 300.0), Duration::from_secs(1)),
41 triangle
42 .rotation
43 .to(360.0_f32.to_radians(), Duration::from_secs(1))
44 ],
45 ]);
46
47 project.show().expect("Failed to render");
48}Additional examples can be found in:
- examples/audio_demo.rs
- examples/images.rs
- examples/group_animation.rs
- examples/grid.rs
- examples/shapes.rs
- examples/camera_demo.rs
- examples/code_animation.rs
- examples/nested_cameras.rs
- examples/math_code.rs
- examples/easing_scope.rs
- examples/signals.rs
- examples/advanced_flow.rs
- examples/anchors.rs
- examples/news_feed.rs
- examples/explainer.rs
Sourcepub fn with_output_path(self, path: &str) -> Self
pub fn with_output_path(self, path: &str) -> Self
Examples found in repository?
examples/export.rs (line 10)
4fn main() {
5 // 1. Initialize for Export
6 let mut project = Project::default()
7 .with_fps(30)
8 .with_ffmpeg(true)
9 .with_title("Export")
10 .with_output_path("output")
11 .close_on_finish();
12
13 // 2. Setup Nodes
14 let circle = Circle::default()
15 .with_position(Vec2::new(400.0, 300.0))
16 .with_radius(50.0)
17 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)); // Blue
18
19 let text = TextNode::default()
20 .with_position(Vec2::new(400.0, 50.0))
21 .with_text("Export Demo")
22 .with_font_size(40.0)
23 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White
24
25 project.scene.add(Box::new(circle.clone()));
26 project.scene.add(Box::new(text.clone()));
27
28 // 3. Define Animations (Color and Font Size)
29 project.scene.video_timeline.add(all![
30 // Circle color and size
31 circle
32 .fill_color
33 .to(Color::rgb8(0xf2, 0xf2, 0xf2), Duration::from_secs(2))
34 .ease(easings::quad_in_out),
35 circle
36 .radius
37 .to(150.0, Duration::from_secs(2))
38 .ease(easings::elastic_out),
39 // Text font size
40 text.font_size
41 .to(50.0, Duration::from_secs(2))
42 .ease(easings::cubic_out),
43 ]);
44
45 // 4. Export (Renders frames and combines them into out.mkv)
46 println!("Starting export to {}...", project.output_path.display());
47 project.export().expect("Failed to export");
48}More examples
examples/advanced_flow.rs (line 11)
4fn main() {
5 // 1. Initialize Project with full API coverage
6 let mut project = Project::default()
7 .with_fps(120)
8 .with_gpu(true)
9 .with_cache(true)
10 .with_ffmpeg(true)
11 .with_output_path("output")
12 .with_title("Advanced Flow")
13 .close_on_finish();
14
15 // 2. Setup Nodes
16 let mut path = BezPath::new();
17 path.move_to((100.0, 300.0));
18 path.curve_to((250.0, 100.0), (550.0, 500.0), (700.0, 300.0));
19 let path_node = PathNode::default()
20 .with_path(path)
21 .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 2.0);
22 let follower = Circle::default()
23 .with_position(Vec2::new(100.0, 300.0))
24 .with_radius(20.0)
25 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
26
27 // Showcase: Rect and Line
28 let background_rect = Rect::default()
29 .with_position(Vec2::new(400.0, 300.0))
30 .with_size(Vec2::new(760.0, 560.0))
31 .with_fill(Color::rgba8(0x33, 0x33, 0x33, 150))
32 .with_radius(20.0);
33
34 let divider_line = Line::default()
35 .with_start(Vec2::new(0.0, 300.0))
36 .with_end(Vec2::new(0.0, 300.0))
37 .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 1.0);
38 let title_text = TextNode::default()
39 .with_position(Vec2::new(50.0, 50.0))
40 .with_anchor(Vec2::new(-1.0, -1.0))
41 .with_text("Motion Canvas in Rust")
42 .with_font_size(40.0)
43 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
44 .with_font("JetBrains Mono");
45
46 let code_block = CodeNode::default()
47 .with_anchor(Vec2::new(-1.0, -1.0))
48 .with_position(Vec2::new(50.0, 415.0))
49 .with_language("rust")
50 .with_opacity(0.0);
51
52 let math_eq = MathNode::default()
53 .with_position(Vec2::new(150.0, 150.0))
54 .with_equation("f(x) = sin(x)")
55 .with_font_size(30.0)
56 .with_fill(Color::rgb8(0xe6, 0xa7, 0x00)); // Yellow
57
58 let logo = ImageNode::default()
59 .with_position(Vec2::new(650.0, 150.0))
60 .with_path("./examples/images/motion-canvas-logo.png")
61 .with_size(Vec2::new(150.0, 150.0));
62
63 project.scene.video_timeline.add(all![
64 // Show code
65 chain![
66 code_block.opacity.to(1.0, Duration::from_secs(1)),
67 code_block.append("fn main() {\n", Duration::from_secs(1)),
68 code_block.append(
69 " let mut engine = MotionCanvas::new();\n",
70 Duration::from_secs(1)
71 ),
72 code_block.append(" engine.render();\n", Duration::from_secs(1)),
73 code_block.append("}", Duration::from_secs(1)),
74 // Staggered appearance of nodes
75 sequence![
76 Duration::from_millis(200),
77 divider_line
78 .end
79 .to(Vec2::new(800.0, 300.0), Duration::from_secs(1))
80 .ease(easings::cubic_out),
81 follower
82 .radius
83 .to(30.0, Duration::from_millis(500))
84 .ease(easings::elastic_out),
85 ],
86 // The path follow combined with a "race" logic
87 any![
88 follower
89 .position
90 .follow(&path_node, Duration::from_secs(3))
91 .ease(easings::cubic_in_out),
92 // Race: if this 'wait' finishes first, the follow is done
93 wait(Duration::from_secs(4)),
94 ],
95 // Final flourishes using different easings
96 all![
97 follower
98 .radius
99 .to(10.0, Duration::from_secs(1))
100 .ease(easings::quad_out),
101 divider_line
102 .start
103 .to(Vec2::new(400.0, 300.0), Duration::from_secs(1))
104 .ease(easings::cubic_in),
105 ]
106 ]
107 ]);
108
109 // 4. Build Scene
110 project.scene.add(Box::new(background_rect.clone()));
111 project.scene.add(Box::new(divider_line.clone()));
112 project.scene.add(Box::new(path_node.clone()));
113 project.scene.add(Box::new(follower.clone()));
114 project.scene.add(Box::new(title_text.clone()));
115 project.scene.add(Box::new(code_block.clone()));
116 project.scene.add(Box::new(math_eq.clone()));
117 project.scene.add(Box::new(logo.clone()));
118
119 // 5. Run (Choose show() for interactive or export() for PNGs)
120 project.show().expect("Failed to render");
121 // project.export().expect("Failed to export");
122}Sourcepub fn with_cache(self, use_cache: bool) -> Self
pub fn with_cache(self, use_cache: bool) -> Self
Examples found in repository?
examples/color_interpolation.rs (line 9)
4fn main() {
5 let mut project = Project::default()
6 .with_dimensions(300, 300)
7 .with_fps(60)
8 .with_title("Color Interpolation")
9 .with_cache(true)
10 .close_on_finish();
11
12 let circle = Circle::default()
13 .with_position(Vec2::new(150.0, 150.0))
14 .with_radius(50.0)
15 .with_fill(Color::RED); // Red
16
17 project.scene.add(Box::new(circle.clone()));
18
19 let duration = Duration::from_secs(1);
20
21 project.scene.video_timeline.add(loop_anim(
22 move || {
23 chain![
24 circle.fill_color.to(Color::YELLOW, duration),
25 circle.fill_color.to(Color::GREEN, duration),
26 circle.fill_color.to(Color::BLUE, duration),
27 circle.fill_color.to(Color::RED, duration),
28 ]
29 },
30 None,
31 ));
32
33 // Show
34 project.show().expect("Failed to render");
35}More examples
examples/getting_started.rs (line 8)
4fn main() {
5 // 1. Initialize the Project
6 let mut project = Project::default()
7 .with_fps(60)
8 .with_cache(true)
9 .with_title("Getting Started")
10 .close_on_finish();
11
12 // 2. Define Nodes
13 let circle = Circle::default()
14 .with_position(Vec2::new(400.0, 300.0))
15 .with_radius(50.0)
16 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
17
18 let text = TextNode::default()
19 .with_position(Vec2::new(400.0, 450.0))
20 .with_text("Hello Rust")
21 .with_font_size(40.0)
22 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White-ish
23
24 // 3. Add Nodes to the Scene
25 project.scene.add(Box::new(circle.clone()));
26 project.scene.add(Box::new(text.clone()));
27
28 // 4. Add Animations to the Timeline
29 project.scene.video_timeline.add(all![
30 circle.radius.to(100.0, Duration::from_secs(1)),
31 text.position
32 .to(Vec2::new(400.0, 500.0), Duration::from_secs(1)),
33 ]);
34
35 // 5. Show
36 project.show().expect("Failed to render");
37}examples/advanced_flow.rs (line 9)
4fn main() {
5 // 1. Initialize Project with full API coverage
6 let mut project = Project::default()
7 .with_fps(120)
8 .with_gpu(true)
9 .with_cache(true)
10 .with_ffmpeg(true)
11 .with_output_path("output")
12 .with_title("Advanced Flow")
13 .close_on_finish();
14
15 // 2. Setup Nodes
16 let mut path = BezPath::new();
17 path.move_to((100.0, 300.0));
18 path.curve_to((250.0, 100.0), (550.0, 500.0), (700.0, 300.0));
19 let path_node = PathNode::default()
20 .with_path(path)
21 .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 2.0);
22 let follower = Circle::default()
23 .with_position(Vec2::new(100.0, 300.0))
24 .with_radius(20.0)
25 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
26
27 // Showcase: Rect and Line
28 let background_rect = Rect::default()
29 .with_position(Vec2::new(400.0, 300.0))
30 .with_size(Vec2::new(760.0, 560.0))
31 .with_fill(Color::rgba8(0x33, 0x33, 0x33, 150))
32 .with_radius(20.0);
33
34 let divider_line = Line::default()
35 .with_start(Vec2::new(0.0, 300.0))
36 .with_end(Vec2::new(0.0, 300.0))
37 .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 1.0);
38 let title_text = TextNode::default()
39 .with_position(Vec2::new(50.0, 50.0))
40 .with_anchor(Vec2::new(-1.0, -1.0))
41 .with_text("Motion Canvas in Rust")
42 .with_font_size(40.0)
43 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
44 .with_font("JetBrains Mono");
45
46 let code_block = CodeNode::default()
47 .with_anchor(Vec2::new(-1.0, -1.0))
48 .with_position(Vec2::new(50.0, 415.0))
49 .with_language("rust")
50 .with_opacity(0.0);
51
52 let math_eq = MathNode::default()
53 .with_position(Vec2::new(150.0, 150.0))
54 .with_equation("f(x) = sin(x)")
55 .with_font_size(30.0)
56 .with_fill(Color::rgb8(0xe6, 0xa7, 0x00)); // Yellow
57
58 let logo = ImageNode::default()
59 .with_position(Vec2::new(650.0, 150.0))
60 .with_path("./examples/images/motion-canvas-logo.png")
61 .with_size(Vec2::new(150.0, 150.0));
62
63 project.scene.video_timeline.add(all![
64 // Show code
65 chain![
66 code_block.opacity.to(1.0, Duration::from_secs(1)),
67 code_block.append("fn main() {\n", Duration::from_secs(1)),
68 code_block.append(
69 " let mut engine = MotionCanvas::new();\n",
70 Duration::from_secs(1)
71 ),
72 code_block.append(" engine.render();\n", Duration::from_secs(1)),
73 code_block.append("}", Duration::from_secs(1)),
74 // Staggered appearance of nodes
75 sequence![
76 Duration::from_millis(200),
77 divider_line
78 .end
79 .to(Vec2::new(800.0, 300.0), Duration::from_secs(1))
80 .ease(easings::cubic_out),
81 follower
82 .radius
83 .to(30.0, Duration::from_millis(500))
84 .ease(easings::elastic_out),
85 ],
86 // The path follow combined with a "race" logic
87 any![
88 follower
89 .position
90 .follow(&path_node, Duration::from_secs(3))
91 .ease(easings::cubic_in_out),
92 // Race: if this 'wait' finishes first, the follow is done
93 wait(Duration::from_secs(4)),
94 ],
95 // Final flourishes using different easings
96 all![
97 follower
98 .radius
99 .to(10.0, Duration::from_secs(1))
100 .ease(easings::quad_out),
101 divider_line
102 .start
103 .to(Vec2::new(400.0, 300.0), Duration::from_secs(1))
104 .ease(easings::cubic_in),
105 ]
106 ]
107 ]);
108
109 // 4. Build Scene
110 project.scene.add(Box::new(background_rect.clone()));
111 project.scene.add(Box::new(divider_line.clone()));
112 project.scene.add(Box::new(path_node.clone()));
113 project.scene.add(Box::new(follower.clone()));
114 project.scene.add(Box::new(title_text.clone()));
115 project.scene.add(Box::new(code_block.clone()));
116 project.scene.add(Box::new(math_eq.clone()));
117 project.scene.add(Box::new(logo.clone()));
118
119 // 5. Run (Choose show() for interactive or export() for PNGs)
120 project.show().expect("Failed to render");
121 // project.export().expect("Failed to export");
122}Sourcepub fn with_ffmpeg(self, use_ffmpeg: bool) -> Self
pub fn with_ffmpeg(self, use_ffmpeg: bool) -> Self
Examples found in repository?
examples/export.rs (line 8)
4fn main() {
5 // 1. Initialize for Export
6 let mut project = Project::default()
7 .with_fps(30)
8 .with_ffmpeg(true)
9 .with_title("Export")
10 .with_output_path("output")
11 .close_on_finish();
12
13 // 2. Setup Nodes
14 let circle = Circle::default()
15 .with_position(Vec2::new(400.0, 300.0))
16 .with_radius(50.0)
17 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)); // Blue
18
19 let text = TextNode::default()
20 .with_position(Vec2::new(400.0, 50.0))
21 .with_text("Export Demo")
22 .with_font_size(40.0)
23 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White
24
25 project.scene.add(Box::new(circle.clone()));
26 project.scene.add(Box::new(text.clone()));
27
28 // 3. Define Animations (Color and Font Size)
29 project.scene.video_timeline.add(all![
30 // Circle color and size
31 circle
32 .fill_color
33 .to(Color::rgb8(0xf2, 0xf2, 0xf2), Duration::from_secs(2))
34 .ease(easings::quad_in_out),
35 circle
36 .radius
37 .to(150.0, Duration::from_secs(2))
38 .ease(easings::elastic_out),
39 // Text font size
40 text.font_size
41 .to(50.0, Duration::from_secs(2))
42 .ease(easings::cubic_out),
43 ]);
44
45 // 4. Export (Renders frames and combines them into out.mkv)
46 println!("Starting export to {}...", project.output_path.display());
47 project.export().expect("Failed to export");
48}More examples
examples/advanced_flow.rs (line 10)
4fn main() {
5 // 1. Initialize Project with full API coverage
6 let mut project = Project::default()
7 .with_fps(120)
8 .with_gpu(true)
9 .with_cache(true)
10 .with_ffmpeg(true)
11 .with_output_path("output")
12 .with_title("Advanced Flow")
13 .close_on_finish();
14
15 // 2. Setup Nodes
16 let mut path = BezPath::new();
17 path.move_to((100.0, 300.0));
18 path.curve_to((250.0, 100.0), (550.0, 500.0), (700.0, 300.0));
19 let path_node = PathNode::default()
20 .with_path(path)
21 .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 2.0);
22 let follower = Circle::default()
23 .with_position(Vec2::new(100.0, 300.0))
24 .with_radius(20.0)
25 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
26
27 // Showcase: Rect and Line
28 let background_rect = Rect::default()
29 .with_position(Vec2::new(400.0, 300.0))
30 .with_size(Vec2::new(760.0, 560.0))
31 .with_fill(Color::rgba8(0x33, 0x33, 0x33, 150))
32 .with_radius(20.0);
33
34 let divider_line = Line::default()
35 .with_start(Vec2::new(0.0, 300.0))
36 .with_end(Vec2::new(0.0, 300.0))
37 .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 1.0);
38 let title_text = TextNode::default()
39 .with_position(Vec2::new(50.0, 50.0))
40 .with_anchor(Vec2::new(-1.0, -1.0))
41 .with_text("Motion Canvas in Rust")
42 .with_font_size(40.0)
43 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
44 .with_font("JetBrains Mono");
45
46 let code_block = CodeNode::default()
47 .with_anchor(Vec2::new(-1.0, -1.0))
48 .with_position(Vec2::new(50.0, 415.0))
49 .with_language("rust")
50 .with_opacity(0.0);
51
52 let math_eq = MathNode::default()
53 .with_position(Vec2::new(150.0, 150.0))
54 .with_equation("f(x) = sin(x)")
55 .with_font_size(30.0)
56 .with_fill(Color::rgb8(0xe6, 0xa7, 0x00)); // Yellow
57
58 let logo = ImageNode::default()
59 .with_position(Vec2::new(650.0, 150.0))
60 .with_path("./examples/images/motion-canvas-logo.png")
61 .with_size(Vec2::new(150.0, 150.0));
62
63 project.scene.video_timeline.add(all![
64 // Show code
65 chain![
66 code_block.opacity.to(1.0, Duration::from_secs(1)),
67 code_block.append("fn main() {\n", Duration::from_secs(1)),
68 code_block.append(
69 " let mut engine = MotionCanvas::new();\n",
70 Duration::from_secs(1)
71 ),
72 code_block.append(" engine.render();\n", Duration::from_secs(1)),
73 code_block.append("}", Duration::from_secs(1)),
74 // Staggered appearance of nodes
75 sequence![
76 Duration::from_millis(200),
77 divider_line
78 .end
79 .to(Vec2::new(800.0, 300.0), Duration::from_secs(1))
80 .ease(easings::cubic_out),
81 follower
82 .radius
83 .to(30.0, Duration::from_millis(500))
84 .ease(easings::elastic_out),
85 ],
86 // The path follow combined with a "race" logic
87 any![
88 follower
89 .position
90 .follow(&path_node, Duration::from_secs(3))
91 .ease(easings::cubic_in_out),
92 // Race: if this 'wait' finishes first, the follow is done
93 wait(Duration::from_secs(4)),
94 ],
95 // Final flourishes using different easings
96 all![
97 follower
98 .radius
99 .to(10.0, Duration::from_secs(1))
100 .ease(easings::quad_out),
101 divider_line
102 .start
103 .to(Vec2::new(400.0, 300.0), Duration::from_secs(1))
104 .ease(easings::cubic_in),
105 ]
106 ]
107 ]);
108
109 // 4. Build Scene
110 project.scene.add(Box::new(background_rect.clone()));
111 project.scene.add(Box::new(divider_line.clone()));
112 project.scene.add(Box::new(path_node.clone()));
113 project.scene.add(Box::new(follower.clone()));
114 project.scene.add(Box::new(title_text.clone()));
115 project.scene.add(Box::new(code_block.clone()));
116 project.scene.add(Box::new(math_eq.clone()));
117 project.scene.add(Box::new(logo.clone()));
118
119 // 5. Run (Choose show() for interactive or export() for PNGs)
120 project.show().expect("Failed to render");
121 // project.export().expect("Failed to export");
122}Sourcepub fn with_gpu(self, use_gpu: bool) -> Self
pub fn with_gpu(self, use_gpu: bool) -> Self
Examples found in repository?
examples/advanced_flow.rs (line 8)
4fn main() {
5 // 1. Initialize Project with full API coverage
6 let mut project = Project::default()
7 .with_fps(120)
8 .with_gpu(true)
9 .with_cache(true)
10 .with_ffmpeg(true)
11 .with_output_path("output")
12 .with_title("Advanced Flow")
13 .close_on_finish();
14
15 // 2. Setup Nodes
16 let mut path = BezPath::new();
17 path.move_to((100.0, 300.0));
18 path.curve_to((250.0, 100.0), (550.0, 500.0), (700.0, 300.0));
19 let path_node = PathNode::default()
20 .with_path(path)
21 .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 2.0);
22 let follower = Circle::default()
23 .with_position(Vec2::new(100.0, 300.0))
24 .with_radius(20.0)
25 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
26
27 // Showcase: Rect and Line
28 let background_rect = Rect::default()
29 .with_position(Vec2::new(400.0, 300.0))
30 .with_size(Vec2::new(760.0, 560.0))
31 .with_fill(Color::rgba8(0x33, 0x33, 0x33, 150))
32 .with_radius(20.0);
33
34 let divider_line = Line::default()
35 .with_start(Vec2::new(0.0, 300.0))
36 .with_end(Vec2::new(0.0, 300.0))
37 .with_stroke(Color::rgb8(0x44, 0x44, 0x44), 1.0);
38 let title_text = TextNode::default()
39 .with_position(Vec2::new(50.0, 50.0))
40 .with_anchor(Vec2::new(-1.0, -1.0))
41 .with_text("Motion Canvas in Rust")
42 .with_font_size(40.0)
43 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
44 .with_font("JetBrains Mono");
45
46 let code_block = CodeNode::default()
47 .with_anchor(Vec2::new(-1.0, -1.0))
48 .with_position(Vec2::new(50.0, 415.0))
49 .with_language("rust")
50 .with_opacity(0.0);
51
52 let math_eq = MathNode::default()
53 .with_position(Vec2::new(150.0, 150.0))
54 .with_equation("f(x) = sin(x)")
55 .with_font_size(30.0)
56 .with_fill(Color::rgb8(0xe6, 0xa7, 0x00)); // Yellow
57
58 let logo = ImageNode::default()
59 .with_position(Vec2::new(650.0, 150.0))
60 .with_path("./examples/images/motion-canvas-logo.png")
61 .with_size(Vec2::new(150.0, 150.0));
62
63 project.scene.video_timeline.add(all![
64 // Show code
65 chain![
66 code_block.opacity.to(1.0, Duration::from_secs(1)),
67 code_block.append("fn main() {\n", Duration::from_secs(1)),
68 code_block.append(
69 " let mut engine = MotionCanvas::new();\n",
70 Duration::from_secs(1)
71 ),
72 code_block.append(" engine.render();\n", Duration::from_secs(1)),
73 code_block.append("}", Duration::from_secs(1)),
74 // Staggered appearance of nodes
75 sequence![
76 Duration::from_millis(200),
77 divider_line
78 .end
79 .to(Vec2::new(800.0, 300.0), Duration::from_secs(1))
80 .ease(easings::cubic_out),
81 follower
82 .radius
83 .to(30.0, Duration::from_millis(500))
84 .ease(easings::elastic_out),
85 ],
86 // The path follow combined with a "race" logic
87 any![
88 follower
89 .position
90 .follow(&path_node, Duration::from_secs(3))
91 .ease(easings::cubic_in_out),
92 // Race: if this 'wait' finishes first, the follow is done
93 wait(Duration::from_secs(4)),
94 ],
95 // Final flourishes using different easings
96 all![
97 follower
98 .radius
99 .to(10.0, Duration::from_secs(1))
100 .ease(easings::quad_out),
101 divider_line
102 .start
103 .to(Vec2::new(400.0, 300.0), Duration::from_secs(1))
104 .ease(easings::cubic_in),
105 ]
106 ]
107 ]);
108
109 // 4. Build Scene
110 project.scene.add(Box::new(background_rect.clone()));
111 project.scene.add(Box::new(divider_line.clone()));
112 project.scene.add(Box::new(path_node.clone()));
113 project.scene.add(Box::new(follower.clone()));
114 project.scene.add(Box::new(title_text.clone()));
115 project.scene.add(Box::new(code_block.clone()));
116 project.scene.add(Box::new(math_eq.clone()));
117 project.scene.add(Box::new(logo.clone()));
118
119 // 5. Run (Choose show() for interactive or export() for PNGs)
120 project.show().expect("Failed to render");
121 // project.export().expect("Failed to export");
122}Sourcepub fn with_background(self, color: Color) -> Self
pub fn with_background(self, color: Color) -> Self
Examples found in repository?
examples/audio_demo.rs (line 8)
4fn main() {
5 let mut project = Project::default()
6 .with_dimensions(800, 450)
7 .with_title("Audio Demo")
8 .with_background(Color::rgb8(20, 20, 25))
9 .close_on_finish();
10
11 // Setup Video Timeline
12 let rect = Rect::new(Vec2::new(100.0, 100.0), Vec2::new(200.0, 200.0), Color::RED)
13 .with_anchor(Vec2::new(-1.0, -1.0));
14
15 project.scene.add(Box::new(rect.clone()));
16
17 project.scene.video_timeline.add(chain!(
18 all![
19 rect.position
20 .to(Vec2::new(150.0, 150.0), Duration::from_secs(1)),
21 rect.size.to(Vec2::new(200.0, 50.0), Duration::from_secs(1)),
22 rect.fill_color.to(Color::BLUE, Duration::from_secs(1)),
23 ],
24 all![
25 rect.position
26 .to(Vec2::new(350.0, 150.0), Duration::from_secs(1)),
27 rect.fill_color.to(Color::RED, Duration::from_secs(1)),
28 ]
29 ));
30
31 // Setup Audio Timeline using new macros and builder
32 project.scene.audio_timeline.add(chain!(
33 play!(AudioNode::new("./examples/audios/combo-1.mp3").with_volume(0.5)),
34 audio_wait!(0.05),
35 play!(AudioNode::new("./examples/audios/combo-2.mp3").with_volume(1.0))
36 ));
37
38 println!("Project configured with separate video and audio timelines.");
39 println!(
40 "Video duration: {:?}",
41 project.scene.video_timeline.duration()
42 );
43 println!(
44 "Audio duration: {:?}",
45 project.scene.audio_timeline.duration()
46 );
47
48 project.show().expect("Failed to run audio demo");
49}More examples
examples/camera_demo.rs (line 7)
4fn main() {
5 let mut project = Project::default()
6 .with_title("Camera Demo")
7 .with_background(Color::rgb8(0x1a, 0x1a, 0x1a))
8 .close_on_finish();
9
10 // Create a grid of circles to move the camera around
11 let mut nodes: Vec<Box<dyn Node>> = Vec::new();
12
13 for x in -2..=2 {
14 for y in -2..=2 {
15 let color = if (x + y) % 2 == 0 {
16 Color::rgb8(0x34, 0x98, 0xdb) // Blue
17 } else {
18 Color::rgb8(0xe7, 0x4c, 0x3c) // Red
19 };
20
21 nodes.push(Box::new(
22 Circle::default()
23 .with_position(Vec2::new(x as f32 * 200.0, y as f32 * 200.0))
24 .with_radius(40.0)
25 .with_fill(color),
26 ));
27 }
28 }
29
30 // Create the camera and add our nodes to it
31 let camera = CameraNode::new(nodes)
32 .with_position(Vec2::new(0.0, 0.0))
33 .with_zoom(1.0);
34
35 project.scene.add(Box::new(camera.clone()));
36
37 // Add HUD text (NOT in camera, so it stays fixed)
38 project.scene.add(Box::new(
39 TextNode::default()
40 .with_position(Vec2::new(400.0, 50.0))
41 .with_text("Camera Control")
42 .with_font_size(60.0)
43 .with_fill(Color::WHITE)
44 .with_anchor(Vec2::ZERO),
45 ));
46
47 // Animation: Pan, Zoom, and Rotate the camera
48 project.scene.video_timeline.add(loop_anim!(
49 chain![
50 wait(Duration::from_secs(1)),
51 // 1. Pan to the right
52 camera
53 .position
54 .to(Vec2::new(200.0, 0.0), Duration::from_secs(1)),
55 // 2. Zoom in
56 camera.zoom.to(2.0, Duration::from_secs(1)),
57 // 3. Pan down and rotate
58 all![
59 camera
60 .position
61 .to(Vec2::new(200.0, 200.0), Duration::from_secs(1)),
62 camera
63 .rotation
64 .to(std::f32::consts::PI / 4.0, Duration::from_secs(1)),
65 ],
66 // 4. Zoom out and reset
67 all![
68 camera.position.to(Vec2::ZERO, Duration::from_secs(1)),
69 camera.zoom.to(0.5, Duration::from_secs(1)),
70 camera.rotation.to(0.0, Duration::from_secs(1)),
71 ],
72 // 5. Back to normal
73 camera.zoom.to(1.0, Duration::from_secs(1)),
74 ],
75 None,
76 ));
77
78 project.show().expect("Failed to render");
79}examples/nested_cameras.rs (line 7)
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}examples/news_feed.rs (line 91)
88fn main() {
89 let mut project = Project::default()
90 .with_fps(60)
91 .with_background(BG)
92 .with_title("News Feed System")
93 .with_dimensions(1920, 1080)
94 .close_on_finish();
95
96 // -- Title --
97 let title = TextNode::default()
98 .with_position(Vec2::new(80.0, 50.0))
99 .with_anchor(Vec2::new(-1.0, -1.0))
100 .with_text("News Feed Architecture")
101 .with_font_size(52.0)
102 .with_font("JetBrains Mono")
103 .with_fill(WHITE)
104 .with_scale(0.95)
105 .with_opacity(0.0);
106
107 // -- Top Tier --
108 let (user_box, user_label) = create_box(
109 "User (Web/App)",
110 Vec2::new(960.0, 130.0),
111 Vec2::new(200.0, 60.0),
112 );
113
114 let (line_lb, lb_end) = create_line(Vec2::new(960.0, 130.0), Vec2::new(960.0, 260.0));
115 let (lb_box, lb_label) = create_box(
116 "Load Balancer",
117 Vec2::new(960.0, 260.0),
118 Vec2::new(160.0, 60.0),
119 );
120
121 let (line_ws, ws_end) = create_line(Vec2::new(960.0, 260.0), Vec2::new(960.0, 390.0));
122 let (ws_box, ws_label) = create_box(
123 "Web Servers",
124 Vec2::new(960.0, 390.0),
125 Vec2::new(300.0, 80.0),
126 );
127
128 // -- Services Tier --
129 let (line_post, post_end) = create_line(Vec2::new(960.0, 390.0), Vec2::new(600.0, 520.0));
130 let (post_box, post_label) = create_box(
131 "Post Service",
132 Vec2::new(600.0, 520.0),
133 Vec2::new(160.0, 60.0),
134 );
135
136 let (line_fanout, fanout_end) = create_line(Vec2::new(960.0, 390.0), Vec2::new(960.0, 520.0));
137 let (fanout_box, fanout_label) = create_box(
138 "Fanout Service",
139 Vec2::new(960.0, 520.0),
140 Vec2::new(180.0, 60.0),
141 );
142
143 let (line_notif, notif_end) = create_line(Vec2::new(960.0, 390.0), Vec2::new(1320.0, 520.0));
144 let (notif_box, notif_label) = create_box(
145 "Notification Service",
146 Vec2::new(1320.0, 520.0),
147 Vec2::new(240.0, 60.0),
148 );
149
150 // -- Post Caches & DB --
151 let (line_post_cache, post_cache_end) =
152 create_line(Vec2::new(600.0, 520.0), Vec2::new(450.0, 720.0));
153 let (post_cache_box, post_cache_label) = create_box(
154 "Post Cache",
155 Vec2::new(450.0, 720.0),
156 Vec2::new(160.0, 80.0),
157 );
158
159 let (line_post_db, post_db_end) = create_line(Vec2::new(600.0, 520.0), Vec2::new(750.0, 720.0));
160 let (post_db_box, post_db_label) =
161 create_box("Post DB", Vec2::new(750.0, 720.0), Vec2::new(140.0, 80.0));
162
163 // -- NF Cache --
164 let (line_nf_cache, nf_cache_end) =
165 create_line(Vec2::new(960.0, 520.0), Vec2::new(960.0, 720.0));
166 let (nf_cache_box, nf_cache_label) = create_box(
167 "News Feed Cache",
168 Vec2::new(960.0, 720.0),
169 Vec2::new(200.0, 80.0),
170 );
171
172 // -- Fanout DBs Flow --
173 let (line_graph_db, graph_db_end) =
174 create_line(Vec2::new(960.0, 520.0), Vec2::new(1280.0, 620.0));
175 let (graph_db_box, graph_db_label) =
176 create_box("Graph DB", Vec2::new(1280.0, 620.0), Vec2::new(140.0, 60.0));
177
178 let (line_user_cache, user_cache_end) =
179 create_line(Vec2::new(960.0, 520.0), Vec2::new(1200.0, 720.0));
180 let (user_cache_box, user_cache_label) = create_box(
181 "User Cache",
182 Vec2::new(1200.0, 720.0),
183 Vec2::new(160.0, 80.0),
184 );
185
186 let (line_user_db, user_db_end) =
187 create_line(Vec2::new(1200.0, 720.0), Vec2::new(1200.0, 860.0));
188 let (user_db_box, user_db_label) =
189 create_box("User DB", Vec2::new(1200.0, 860.0), Vec2::new(140.0, 80.0));
190
191 // -- Case Indicators --
192 let case1 = TextNode::default()
193 .with_position(Vec2::new(80.0, 110.0))
194 .with_text("Scenario 1: Cache Miss (Data Fallback)")
195 .with_anchor(Vec2::new(-1.0, -1.0))
196 .with_font_size(24.0)
197 .with_font("JetBrains Mono")
198 .with_fill(YELLOW)
199 .with_opacity(0.0);
200
201 let case2 = TextNode::default()
202 .with_position(Vec2::new(80.0, 110.0))
203 .with_text("Scenario 2: Cache Hit (Fast Path)")
204 .with_anchor(Vec2::new(-1.0, -1.0))
205 .with_font_size(24.0)
206 .with_font("JetBrains Mono")
207 .with_fill(GREEN)
208 .with_opacity(0.0);
209
210 let case3 = TextNode::default()
211 .with_position(Vec2::new(80.0, 110.0))
212 .with_text("Scenario 3: Rate Limited (Packet Dropped)")
213 .with_font_size(24.0)
214 .with_anchor(Vec2::new(-1.0, -1.0))
215 .with_font("JetBrains Mono")
216 .with_fill(RED)
217 .with_opacity(0.0);
218
219 // -- Flow Step Indicators --
220
221 let case4 = TextNode::default()
222 .with_position(Vec2::new(80.0, 110.0))
223 .with_text("Scenario 4: Full Publish (Parallel)")
224 .with_font_size(24.0)
225 .with_anchor(Vec2::new(-1.0, -1.0))
226 .with_font("JetBrains Mono")
227 .with_fill(GREEN)
228 .with_opacity(0.0);
229
230 let step1 = TextNode::default()
231 .with_position(Vec2::new(1120.0, 570.0))
232 .with_text("1: Get Friend IDs")
233 .with_font_size(16.0)
234 .with_font("JetBrains Mono")
235 .with_fill(YELLOW)
236 .with_opacity(0.0);
237
238 let step2 = TextNode::default()
239 .with_position(Vec2::new(1060.0, 600.0))
240 .with_text("2: Get Friend Data")
241 .with_font_size(16.0)
242 .with_font("JetBrains Mono")
243 .with_fill(YELLOW)
244 .with_opacity(0.0);
245
246 let step3 = TextNode::default()
247 .with_position(Vec2::new(960.0, 620.0))
248 .with_text("3: Update Feed")
249 .with_font_size(16.0)
250 .with_font("JetBrains Mono")
251 .with_fill(YELLOW)
252 .with_opacity(0.0);
253
254 // Initial packet using exact center point
255 let packet = Circle::default()
256 .with_position(Vec2::new(960.0, 130.0))
257 .with_radius(12.0)
258 .with_scale(0.95)
259 .with_opacity(0.0)
260 .with_fill(GREEN);
261
262 let packet_post = Circle::default()
263 .with_position(Vec2::new(960.0, 130.0))
264 .with_radius(12.0)
265 .with_scale(0.95)
266 .with_opacity(0.0)
267 .with_fill(ACCENT);
268
269 let packet_notif = Circle::default()
270 .with_position(Vec2::new(960.0, 130.0))
271 .with_radius(12.0)
272 .with_scale(0.95)
273 .with_opacity(0.0)
274 .with_fill(YELLOW);
275
276 let fast_in = easings::cubic_out;
277 let smooth = easings::cubic_in_out;
278 let appear_dur = Duration::from_millis(250);
279 let move_dur = Duration::from_millis(700);
280
281 // -- Sequence --
282 project.scene.video_timeline.add(chain![
283 wait(Duration::from_millis(500)),
284 all![
285 show(&title, appear_dur),
286 title
287 .scale
288 .to(Vec2::new(1.0, 1.0), appear_dur)
289 .ease(fast_in)
290 ],
291 // Topology Staggered
292 sequence![
293 Duration::from_millis(60),
294 all![
295 show(&user_box, appear_dur),
296 user_box
297 .scale
298 .to(Vec2::new(1.0, 1.0), appear_dur)
299 .ease(fast_in),
300 show(&user_label, appear_dur),
301 user_label
302 .scale
303 .to(Vec2::new(1.0, 1.0), appear_dur)
304 .ease(fast_in),
305 ],
306 all![
307 show(&line_lb, appear_dur),
308 line_lb.end.to(lb_end, appear_dur).ease(smooth),
309 show(&lb_box, appear_dur),
310 lb_box
311 .scale
312 .to(Vec2::new(1.0, 1.0), appear_dur)
313 .ease(fast_in),
314 show(&lb_label, appear_dur),
315 lb_label
316 .scale
317 .to(Vec2::new(1.0, 1.0), appear_dur)
318 .ease(fast_in),
319 ],
320 all![
321 show(&line_ws, appear_dur),
322 line_ws.end.to(ws_end, appear_dur).ease(smooth),
323 show(&ws_box, appear_dur),
324 ws_box
325 .scale
326 .to(Vec2::new(1.0, 1.0), appear_dur)
327 .ease(fast_in),
328 show(&ws_label, appear_dur),
329 ws_label
330 .scale
331 .to(Vec2::new(1.0, 1.0), appear_dur)
332 .ease(fast_in),
333 ]
334 ],
335 sequence![
336 Duration::from_millis(60),
337 all![
338 show(&line_post, appear_dur),
339 line_post.end.to(post_end, appear_dur).ease(smooth),
340 show(&post_box, appear_dur),
341 post_box
342 .scale
343 .to(Vec2::new(1.0, 1.0), appear_dur)
344 .ease(fast_in),
345 show(&post_label, appear_dur),
346 post_label
347 .scale
348 .to(Vec2::new(1.0, 1.0), appear_dur)
349 .ease(fast_in),
350 ],
351 all![
352 show(&line_fanout, appear_dur),
353 line_fanout.end.to(fanout_end, appear_dur).ease(smooth),
354 show(&fanout_box, appear_dur),
355 fanout_box
356 .scale
357 .to(Vec2::new(1.0, 1.0), appear_dur)
358 .ease(fast_in),
359 show(&fanout_label, appear_dur),
360 fanout_label
361 .scale
362 .to(Vec2::new(1.0, 1.0), appear_dur)
363 .ease(fast_in),
364 ],
365 all![
366 show(&line_notif, appear_dur),
367 line_notif.end.to(notif_end, appear_dur).ease(smooth),
368 show(¬if_box, appear_dur),
369 notif_box
370 .scale
371 .to(Vec2::new(1.0, 1.0), appear_dur)
372 .ease(fast_in),
373 show(¬if_label, appear_dur),
374 notif_label
375 .scale
376 .to(Vec2::new(1.0, 1.0), appear_dur)
377 .ease(fast_in),
378 ],
379 ],
380 sequence![
381 Duration::from_millis(60),
382 all![
383 show(&line_post_cache, appear_dur),
384 line_post_cache
385 .end
386 .to(post_cache_end, appear_dur)
387 .ease(smooth),
388 show(&post_cache_box, appear_dur),
389 post_cache_box
390 .scale
391 .to(Vec2::new(1.0, 1.0), appear_dur)
392 .ease(fast_in),
393 show(&post_cache_label, appear_dur),
394 post_cache_label
395 .scale
396 .to(Vec2::new(1.0, 1.0), appear_dur)
397 .ease(fast_in),
398 ],
399 all![
400 show(&line_post_db, appear_dur),
401 line_post_db.end.to(post_db_end, appear_dur).ease(smooth),
402 show(&post_db_box, appear_dur),
403 post_db_box
404 .scale
405 .to(Vec2::new(1.0, 1.0), appear_dur)
406 .ease(fast_in),
407 show(&post_db_label, appear_dur),
408 post_db_label
409 .scale
410 .to(Vec2::new(1.0, 1.0), appear_dur)
411 .ease(fast_in),
412 ],
413 all![
414 show(&line_nf_cache, appear_dur),
415 line_nf_cache.end.to(nf_cache_end, appear_dur).ease(smooth),
416 show(&nf_cache_box, appear_dur),
417 nf_cache_box
418 .scale
419 .to(Vec2::new(1.0, 1.0), appear_dur)
420 .ease(fast_in),
421 show(&nf_cache_label, appear_dur),
422 nf_cache_label
423 .scale
424 .to(Vec2::new(1.0, 1.0), appear_dur)
425 .ease(fast_in),
426 ],
427 all![
428 show(&line_graph_db, appear_dur),
429 line_graph_db.end.to(graph_db_end, appear_dur).ease(smooth),
430 show(&graph_db_box, appear_dur),
431 graph_db_box
432 .scale
433 .to(Vec2::new(1.0, 1.0), appear_dur)
434 .ease(fast_in),
435 show(&graph_db_label, appear_dur),
436 graph_db_label
437 .scale
438 .to(Vec2::new(1.0, 1.0), appear_dur)
439 .ease(fast_in),
440 ],
441 all![
442 show(&line_user_cache, appear_dur),
443 line_user_cache
444 .end
445 .to(user_cache_end, appear_dur)
446 .ease(smooth),
447 show(&user_cache_box, appear_dur),
448 user_cache_box
449 .scale
450 .to(Vec2::new(1.0, 1.0), appear_dur)
451 .ease(fast_in),
452 show(&user_cache_label, appear_dur),
453 user_cache_label
454 .scale
455 .to(Vec2::new(1.0, 1.0), appear_dur)
456 .ease(fast_in),
457 ],
458 all![
459 show(&line_user_db, appear_dur),
460 line_user_db.end.to(user_db_end, appear_dur).ease(smooth),
461 show(&user_db_box, appear_dur),
462 user_db_box
463 .scale
464 .to(Vec2::new(1.0, 1.0), appear_dur)
465 .ease(fast_in),
466 show(&user_db_label, appear_dur),
467 user_db_label
468 .scale
469 .to(Vec2::new(1.0, 1.0), appear_dur)
470 .ease(fast_in),
471 ]
472 ],
473 wait(Duration::from_secs(1)),
474 // ==========================================
475 // SCENARIO 1: CACHE MISS
476 // ==========================================
477 all![
478 show(&case1, appear_dur),
479 show(&packet, Duration::from_millis(200)),
480 packet.fill_color.to(GREEN, Duration::from_millis(0)), // Start healthy
481 packet
482 .scale
483 .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
484 .ease(fast_in),
485 packet
486 .position
487 .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
488 ],
489 packet
490 .position
491 .to(Vec2::new(960.0, 260.0), move_dur)
492 .ease(smooth),
493 packet
494 .position
495 .to(Vec2::new(960.0, 390.0), move_dur)
496 .ease(smooth),
497 packet
498 .position
499 .to(Vec2::new(960.0, 520.0), move_dur)
500 .ease(smooth),
501 wait(Duration::from_millis(200)),
502 // 1: Graph DB Check
503 all![
504 packet
505 .position
506 .to(Vec2::new(1280.0, 620.0), move_dur)
507 .ease(smooth),
508 delay![move_dur / 2, show(&step1, appear_dur)]
509 ],
510 packet
511 .position
512 .to(Vec2::new(960.0, 520.0), move_dur)
513 .ease(smooth),
514 // 2: User Cache MISSED -> Query DB
515 all![
516 packet
517 .position
518 .to(Vec2::new(1200.0, 720.0), move_dur)
519 .ease(smooth),
520 delay![move_dur / 2, show(&step2, appear_dur)]
521 ],
522 // Alert Miss: Flash Yellow and head to DB
523 all![
524 packet.fill_color.to(YELLOW, Duration::from_millis(200)),
525 packet
526 .scale
527 .to(Vec2::new(1.2, 1.2), Duration::from_millis(200))
528 .ease(easings::bounce_out),
529 delay![
530 Duration::from_millis(200),
531 packet
532 .position
533 .to(Vec2::new(1200.0, 860.0), move_dur)
534 .ease(smooth)
535 ]
536 ],
537 // Return from DB to Fanout smoothly
538 all![
539 packet.fill_color.to(GREEN, Duration::from_millis(200)),
540 packet
541 .scale
542 .to(Vec2::new(1.0, 1.0), Duration::from_millis(200)),
543 packet
544 .position
545 .to(Vec2::new(1200.0, 720.0), move_dur)
546 .ease(smooth)
547 ],
548 packet
549 .position
550 .to(Vec2::new(960.0, 520.0), move_dur)
551 .ease(smooth),
552 // 3: Write to News Feed Cache
553 all![
554 packet
555 .position
556 .to(Vec2::new(960.0, 720.0), move_dur)
557 .ease(smooth),
558 delay![move_dur / 2, show(&step3, appear_dur)]
559 ],
560 packet
561 .position
562 .to(Vec2::new(960.0, 520.0), move_dur)
563 .ease(smooth),
564 wait(Duration::from_millis(200)),
565 // Packet Flow (Back Track to User)
566 packet
567 .position
568 .to(Vec2::new(960.0, 390.0), move_dur)
569 .ease(smooth),
570 packet
571 .position
572 .to(Vec2::new(960.0, 260.0), move_dur)
573 .ease(smooth),
574 packet
575 .position
576 .to(Vec2::new(960.0, 130.0), move_dur)
577 .ease(smooth),
578 // Hide packet & Case states
579 all![
580 hide(&packet, Duration::from_millis(200)),
581 hide(&case1, appear_dur),
582 hide(&step1, appear_dur),
583 hide(&step2, appear_dur),
584 hide(&step3, appear_dur),
585 ],
586 wait(Duration::from_secs(1)),
587 // ==========================================
588 // SCENARIO 2: CACHE HIT
589 // ==========================================
590 all![
591 show(&case2, appear_dur),
592 show(&packet, Duration::from_millis(200)),
593 packet
594 .position
595 .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
596 packet
597 .scale
598 .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
599 .ease(fast_in),
600 ],
601 packet
602 .position
603 .to(Vec2::new(960.0, 260.0), move_dur)
604 .ease(smooth),
605 packet
606 .position
607 .to(Vec2::new(960.0, 390.0), move_dur)
608 .ease(smooth),
609 packet
610 .position
611 .to(Vec2::new(960.0, 520.0), move_dur)
612 .ease(smooth),
613 // 1: Graph DB
614 all![
615 packet
616 .position
617 .to(Vec2::new(1280.0, 620.0), move_dur)
618 .ease(smooth),
619 delay![move_dur / 2, show(&step1, appear_dur)]
620 ],
621 packet
622 .position
623 .to(Vec2::new(960.0, 520.0), move_dur)
624 .ease(smooth),
625 // 2: User Cache HIT (Skip DB entirely)
626 all![
627 packet
628 .position
629 .to(Vec2::new(1200.0, 720.0), move_dur)
630 .ease(smooth),
631 delay![move_dur / 2, show(&step2, appear_dur)]
632 ],
633 packet
634 .position
635 .to(Vec2::new(960.0, 520.0), move_dur)
636 .ease(smooth),
637 // 3: News Feed Cache
638 all![
639 packet
640 .position
641 .to(Vec2::new(960.0, 720.0), move_dur)
642 .ease(smooth),
643 delay![move_dur / 2, show(&step3, appear_dur)]
644 ],
645 packet
646 .position
647 .to(Vec2::new(960.0, 520.0), move_dur)
648 .ease(smooth),
649 // Return
650 packet
651 .position
652 .to(Vec2::new(960.0, 390.0), move_dur)
653 .ease(smooth),
654 packet
655 .position
656 .to(Vec2::new(960.0, 260.0), move_dur)
657 .ease(smooth),
658 packet
659 .position
660 .to(Vec2::new(960.0, 130.0), move_dur)
661 .ease(smooth),
662 all![
663 hide(&packet, Duration::from_millis(200)),
664 hide(&case2, appear_dur),
665 hide(&step1, appear_dur),
666 hide(&step2, appear_dur),
667 hide(&step3, appear_dur),
668 ],
669 wait(Duration::from_secs(1)),
670 // ==========================================
671 // SCENARIO 3: DROPPED PACKET
672 // ==========================================
673 all![
674 show(&case3, appear_dur),
675 show(&packet, Duration::from_millis(200)),
676 packet.fill_color.to(GREEN, Duration::from_millis(0)),
677 packet
678 .position
679 .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
680 packet
681 .scale
682 .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
683 .ease(fast_in),
684 ],
685 // To LB
686 packet
687 .position
688 .to(Vec2::new(960.0, 260.0), move_dur)
689 .ease(smooth),
690 // Moving to Web Servers but fails mid-way / at LB
691 all![
692 packet
693 .position
694 .to(Vec2::new(960.0, 390.0), move_dur)
695 .ease(smooth),
696 // Turns Red and gets destroyed
697 sequence![
698 move_dur / 2, // Wait until halfway
699 all![
700 packet.fill_color.to(RED, Duration::from_millis(150)),
701 packet
702 .scale
703 .to(Vec2::new(1.8, 1.8), Duration::from_millis(150))
704 .ease(easings::elastic_out),
705 ],
706 all![
707 hide(&packet, Duration::from_millis(200)),
708 packet
709 .scale
710 .to(Vec2::new(0.0, 0.0), Duration::from_millis(200))
711 .ease(easings::cubic_in),
712 ]
713 ]
714 ],
715 hide(&case3, appear_dur),
716 wait(Duration::from_secs(1)),
717 // ==========================================
718 // SCENARIO 4: PARALLEL PUBLISH
719 // ==========================================
720 all![
721 show(&case4, appear_dur),
722 show(&packet, Duration::from_millis(200)),
723 show(&packet_post, Duration::from_millis(200)),
724 show(&packet_notif, Duration::from_millis(200)),
725 packet.fill_color.to(GREEN, Duration::from_millis(0)),
726 packet
727 .position
728 .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
729 packet
730 .scale
731 .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
732 .ease(fast_in),
733 packet_post
734 .position
735 .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
736 packet_post
737 .scale
738 .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
739 .ease(fast_in),
740 packet_notif
741 .position
742 .to(Vec2::new(960.0, 130.0), Duration::from_millis(0)),
743 packet_notif
744 .scale
745 .to(Vec2::new(1.0, 1.0), Duration::from_millis(200))
746 .ease(fast_in),
747 ],
748 // Flow down collectively to Web Servers
749 all![
750 packet
751 .position
752 .to(Vec2::new(960.0, 260.0), move_dur)
753 .ease(smooth),
754 packet_post
755 .position
756 .to(Vec2::new(960.0, 260.0), move_dur)
757 .ease(smooth),
758 packet_notif
759 .position
760 .to(Vec2::new(960.0, 260.0), move_dur)
761 .ease(smooth),
762 ],
763 all![
764 packet
765 .position
766 .to(Vec2::new(960.0, 390.0), move_dur)
767 .ease(smooth),
768 packet_post
769 .position
770 .to(Vec2::new(960.0, 390.0), move_dur)
771 .ease(smooth),
772 packet_notif
773 .position
774 .to(Vec2::new(960.0, 390.0), move_dur)
775 .ease(smooth),
776 ],
777 // BRANCHING!
778 all![
779 // Main packet goes to fanout
780 packet
781 .position
782 .to(Vec2::new(960.0, 520.0), move_dur)
783 .ease(smooth),
784 // Post packet to Post Service
785 packet_post
786 .position
787 .to(Vec2::new(600.0, 520.0), move_dur)
788 .ease(smooth),
789 // Notif packet to Notification Service
790 packet_notif
791 .position
792 .to(Vec2::new(1320.0, 520.0), move_dur)
793 .ease(smooth),
794 ],
795 wait(Duration::from_millis(200)),
796 // Post DB, NF Cache, Notifications APNS fly off
797 all![
798 // Fanout updates NF Cache
799 packet
800 .position
801 .to(Vec2::new(960.0, 720.0), move_dur)
802 .ease(smooth),
803 // Post updates DB
804 packet_post
805 .position
806 .to(Vec2::new(750.0, 720.0), move_dur)
807 .ease(smooth),
808 // Notif flies off-screen (Apple Push Notification Server etc)
809 packet_notif
810 .position
811 .to(Vec2::new(1500.0, 520.0), move_dur)
812 .ease(smooth),
813 hide(&packet_notif, move_dur),
814 ],
815 wait(Duration::from_millis(200)),
816 // Return
817 all![
818 packet
819 .position
820 .to(Vec2::new(960.0, 520.0), move_dur)
821 .ease(smooth),
822 packet_post
823 .position
824 .to(Vec2::new(600.0, 520.0), move_dur)
825 .ease(smooth),
826 ],
827 all![
828 packet
829 .position
830 .to(Vec2::new(960.0, 390.0), move_dur)
831 .ease(smooth),
832 packet_post
833 .position
834 .to(Vec2::new(960.0, 390.0), move_dur)
835 .ease(smooth),
836 ],
837 all![
838 packet
839 .position
840 .to(Vec2::new(960.0, 260.0), move_dur)
841 .ease(smooth),
842 packet_post
843 .position
844 .to(Vec2::new(960.0, 260.0), move_dur)
845 .ease(smooth),
846 ],
847 all![
848 packet
849 .position
850 .to(Vec2::new(960.0, 130.0), move_dur)
851 .ease(smooth),
852 packet_post
853 .position
854 .to(Vec2::new(960.0, 130.0), move_dur)
855 .ease(smooth),
856 ],
857 // Hide
858 all![
859 hide(&packet, Duration::from_millis(200)),
860 hide(&packet_post, Duration::from_millis(200)),
861 hide(&case4, appear_dur),
862 ],
863 wait(Duration::from_secs(2)),
864 ]);
865
866 // -- Adding to Scene --
867 for element in [
868 Box::new(title) as Box<dyn Node>,
869 Box::new(line_lb),
870 Box::new(line_ws),
871 Box::new(line_post),
872 Box::new(line_fanout),
873 Box::new(line_notif),
874 Box::new(line_post_cache),
875 Box::new(line_post_db),
876 Box::new(line_nf_cache),
877 Box::new(line_graph_db),
878 Box::new(line_user_cache),
879 Box::new(line_user_db),
880 Box::new(user_box),
881 Box::new(user_label),
882 Box::new(lb_box),
883 Box::new(lb_label),
884 Box::new(ws_box),
885 Box::new(ws_label),
886 Box::new(post_box),
887 Box::new(post_label),
888 Box::new(fanout_box),
889 Box::new(fanout_label),
890 Box::new(notif_box),
891 Box::new(notif_label),
892 Box::new(post_cache_box),
893 Box::new(post_cache_label),
894 Box::new(post_db_box),
895 Box::new(post_db_label),
896 Box::new(nf_cache_box),
897 Box::new(nf_cache_label),
898 Box::new(graph_db_box),
899 Box::new(graph_db_label),
900 Box::new(user_cache_box),
901 Box::new(user_cache_label),
902 Box::new(user_db_box),
903 Box::new(user_db_label),
904 Box::new(case1),
905 Box::new(case2),
906 Box::new(case3),
907 Box::new(step1),
908 Box::new(step2),
909 Box::new(step3),
910 Box::new(case4),
911 Box::new(packet_post),
912 Box::new(packet_notif),
913 Box::new(packet),
914 ] {
915 project.scene.add(element);
916 }
917
918 project.show().expect("Failed to render");
919}examples/explainer.rs (line 153)
148fn main() {
149 let mut project = Project::default()
150 .with_dimensions(CANVAS_W, CANVAS_H)
151 .with_fps(60)
152 .with_title("Explainer")
153 .with_background(BG)
154 .close_on_finish();
155
156 // =====================================================================
157 // S1: TITLE CARD
158 // =====================================================================
159 let s1_line = hline(100.0);
160 let s1_title = TextNode::default()
161 .with_anchor(Vec2::new(-1.0, -1.0))
162 .with_position(Vec2::new(LEFT, 120.0))
163 .with_text("motion-canvas-rs")
164 .with_font_size(52.0)
165 .with_fill(ACCENT)
166 .with_font(FONT)
167 .with_opacity(0.0);
168 let s1_sub = h2("A GPU-Accelerated Vector Animation Engine", 185.0);
169 let s1_built = body(
170 "Built on Vello + Typst — Inspired by Motion Canvas",
171 220.0,
172 );
173 let s1_desc = body(
174 "This animation will teach you how the library works,",
175 280.0,
176 );
177 let s1_desc2 = body(
178 "from struct definitions to the GPU rendering pipeline.",
179 305.0,
180 );
181
182 let s1_logo = ImageNode::default()
183 .with_position(Vec2::new(950.0, 420.0))
184 .with_path("examples/images/motion-canvas-rs.svg")
185 .with_scale(0.3)
186 .with_opacity(0.0);
187
188 for n in [&s1_title, &s1_sub, &s1_built, &s1_desc, &s1_desc2] {
189 project.scene.add(Box::new(n.clone()));
190 }
191 project.scene.add(Box::new(s1_line.clone()));
192 project.scene.add(Box::new(s1_logo.clone()));
193
194 // =====================================================================
195 // S2: THE 5 STEPS
196 // =====================================================================
197 let s2_h = title("Every program follows 5 steps", 50.0);
198 let steps = [
199 "1. Create a Project — your canvas settings",
200 "2. Create Nodes — shapes, text, images",
201 "3. Add Nodes to Scene — what gets drawn",
202 "4. Animate the Timeline — how things move",
203 "5. Show or Export — live window or video",
204 ];
205 let s2_texts: Vec<TextNode> = steps
206 .iter()
207 .enumerate()
208 .map(|(i, s)| body(s, 120.0 + i as f32 * 35.0))
209 .collect();
210
211 project.scene.add(Box::new(s2_h.clone()));
212 for t in &s2_texts {
213 project.scene.add(Box::new(t.clone()));
214 }
215
216 // =====================================================================
217 // S3: WHAT IS A STRUCT? + The Project struct
218 // =====================================================================
219 let s3_h = title("What is a 'struct'?", 50.0);
220 let s3_explain = h2(
221 "A struct is a container that groups related data together.",
222 95.0,
223 );
224 let s3_analogy = body(
225 "Think of it like a class in Python/JS, but it only holds data.",
226 130.0,
227 );
228
229 let s3_code = code_block(
230 "pub struct Project {
231 pub width: u32, // Canvas width in pixels
232 pub height: u32, // Canvas height in pixels
233 pub fps: u32, // Frames per second
234 pub title: String, // Window title
235 pub scene: BaseScene, // Holds nodes + timelines
236 pub background_color: Color,
237 pub close_on_finish: bool,
238}",
239 175.0,
240 );
241
242 let s3_note = note(
243 "^ This is the actual Project struct from the library.",
244 420.0,
245 );
246 let s3_note2 = body(
247 "'pub' means public — anyone can read/write these fields.",
248 455.0,
249 );
250 let s3_note3 = body(
251 "u32 = unsigned 32-bit integer, String = text, bool = true/false",
252 485.0,
253 );
254
255 for n in [
256 &s3_h,
257 &s3_explain,
258 &s3_analogy,
259 &s3_note,
260 &s3_note2,
261 &s3_note3,
262 ] {
263 project.scene.add(Box::new(n.clone()));
264 }
265 project.scene.add(Box::new(s3_code.clone()));
266
267 // =====================================================================
268 // S4: WHAT IS impl? + Builder pattern
269 // =====================================================================
270 let s4_h = title("What is 'impl'?", 50.0);
271 let s4_explain = h2("impl adds methods (functions) to a struct.", 95.0);
272 let s4_analogy = body(
273 "Like adding methods to a class. Separated from the data.",
274 130.0,
275 );
276
277 let s4_code = code_block(
278 "impl Project {
279 pub fn with_fps(mut self, fps: u32) -> Self {
280 self.fps = fps; // set the value
281 self // return yourself (builder pattern)
282 }
283 pub fn with_title(mut self, title: &str) -> Self {
284 self.title = title.to_string();
285 self
286 }
287}",
288 175.0,
289 );
290
291 let s4_usage = body("Usage — chain calls to configure:", 430.0);
292 let s4_usage_code = code_block(
293 "let project = Project::default()
294 .with_fps(60)
295 .with_title(\"My Animation\")
296 .with_dimensions(800, 600)
297 .close_on_finish();",
298 460.0,
299 );
300
301 let s4_note = note(
302 "Each .with_*() returns 'self', so you can chain them.",
303 590.0,
304 );
305
306 for n in [&s4_h, &s4_explain, &s4_analogy, &s4_usage, &s4_note] {
307 project.scene.add(Box::new(n.clone()));
308 }
309 project.scene.add(Box::new(s4_code.clone()));
310 project.scene.add(Box::new(s4_usage_code.clone()));
311
312 // =====================================================================
313 // S5: WHAT IS A TRAIT? + The Node trait
314 // =====================================================================
315 let s5_h = title("What is a 'trait'?", 50.0);
316 let s5_explain = h2(
317 "A trait is a contract — like an interface in Java/TypeScript.",
318 95.0,
319 );
320 let s5_analogy = body(
321 "Any type that implements a trait promises to provide those methods.",
322 130.0,
323 );
324
325 let s5_code = code_block(
326 "pub trait Node: Send + Sync + 'static {
327 fn render(&self, scene: &mut Scene,
328 parent_transform: Affine,
329 parent_opacity: f32);
330 fn update(&mut self, dt: Duration);
331 fn state_hash(&self) -> u64;
332 fn clone_node(&self) -> Box<dyn Node>;
333}",
334 175.0,
335 );
336
337 let s5_r = note(
338 "render() — draw yourself using current signal values",
339 410.0,
340 );
341 let s5_u = note(
342 "update(dt) — called every frame (for per-frame logic)",
343 435.0,
344 );
345 let s5_s = note(
346 "state_hash() — returns a number that changes when you change",
347 460.0,
348 );
349 let s5_c = note("clone_node() — make a deep copy of yourself", 485.0);
350 let s5_every = body(
351 "Circle, Rect, Line, TextNode, Polygon all implement Node.",
352 530.0,
353 );
354
355 for n in [
356 &s5_h,
357 &s5_explain,
358 &s5_analogy,
359 &s5_r,
360 &s5_u,
361 &s5_s,
362 &s5_c,
363 &s5_every,
364 ] {
365 project.scene.add(Box::new(n.clone()));
366 }
367 project.scene.add(Box::new(s5_code.clone()));
368
369 // =====================================================================
370 // S6: NODE GALLERY (visual demo)
371 // =====================================================================
372 let s6_h = title("The Built-in Nodes", 50.0);
373 let s6_sub = body(
374 "Each node uses the builder pattern and stores properties as Signals.",
375 90.0,
376 );
377
378 let demo_c = Circle::default()
379 .with_position(Vec2::new(120.0, 230.0))
380 .with_radius(40.0)
381 .with_fill(RED)
382 .with_opacity(0.0);
383 let demo_r = Rect::default()
384 .with_position(Vec2::new(293.0, 230.0))
385 .with_size(Vec2::new(80.0, 80.0))
386 .with_fill(ACCENT)
387 .with_radius(8.0)
388 .with_opacity(0.0);
389 let demo_l = Line::default()
390 .with_start(Vec2::new(420.0, 200.0))
391 .with_end(Vec2::new(510.0, 270.0))
392 .with_stroke(WHITE, 3.0)
393 .with_opacity(0.0);
394 let demo_p = Polygon::regular(5, 40.0)
395 .with_position(Vec2::new(606.0, 230.0))
396 .with_fill(YELLOW)
397 .with_opacity(0.0);
398 let demo_t = TextNode::default()
399 .with_position(Vec2::new(765.0, 230.0))
400 .with_text("Abc")
401 .with_font_size(36.0)
402 .with_fill(GREEN)
403 .with_font(FONT)
404 .with_opacity(0.0);
405
406 let lc = dim("Circle", 95.0, 285.0);
407 let lr = dim("Rect", 280.0, 285.0);
408 let ll = dim("Line", 445.0, 285.0);
409 let lp = dim("Polygon", 580.0, 285.0);
410 let lt = dim("TextNode", 735.0, 285.0);
411
412 let s6_box_h = h2("Why Box<dyn Node>?", 340.0);
413 let s6_box1 = body("The scene stores different node types in one list:", 375.0);
414 let s6_box_code = code_block(
415 "pub struct BaseScene {
416 pub nodes: Vec<Box<dyn Node>>, // a list of \"any Node\"
417}
418// 'Box' = heap-allocated, 'dyn Node' = any type implementing Node
419// Like List<INode> in Java or Array<Node> in TypeScript
420project.scene.add(Box::new(circle)); // wrap + add",
421 405.0,
422 );
423
424 for n in [&s6_h, &s6_sub, &lc, &lr, &ll, &lp, <, &s6_box_h, &s6_box1] {
425 project.scene.add(Box::new(n.clone()));
426 }
427 project.scene.add(Box::new(demo_c.clone()));
428 project.scene.add(Box::new(demo_r.clone()));
429 project.scene.add(Box::new(demo_l.clone()));
430 project.scene.add(Box::new(demo_p.clone()));
431 project.scene.add(Box::new(demo_t.clone()));
432 project.scene.add(Box::new(s6_box_code.clone()));
433
434 // =====================================================================
435 // S7: SIGNALS — The Reactive Core
436 // =====================================================================
437 let s7_h = title("Signals — The Reactive Core", 50.0);
438 let s7_sub = h2("Every animatable property is a Signal<T>.", 95.0);
439
440 let s7_code = code_block(
441 "pub struct Signal<T> {
442 pub data: Arc<Mutex<SignalData<T>>>,
443}
444pub struct SignalData<T> {
445 pub value: T, // the actual value (f32, Vec2, Color...)
446}",
447 140.0,
448 );
449
450 let s7_arc = note("Arc = shared pointer. Multiple owners, same data.", 310.0);
451 let s7_mutex = note(
452 "Mutex = lock. Only one thread reads/writes at a time.",
453 335.0,
454 );
455 let s7_why = body(
456 "Why? A node and its animation both need the same property:",
457 380.0,
458 );
459
460 let s7_diagram_code = code_block(
461 "let circle = Circle::default().with_radius(50.0);
462// circle.radius is a Signal<f32>
463
464circle.radius.to(100.0, Duration::from_secs(1));
465// Creates a SignalTween with a CLONE of circle.radius
466// Both point to the SAME underlying value (via Arc)
467
468// The animation WRITES new values each frame
469// The node READS them when rendering",
470 420.0,
471 );
472
473 // Live demo circle
474 let sig_demo = Circle::default()
475 .with_position(Vec2::new(1000.0, 400.0))
476 .with_radius(50.0)
477 .with_fill(RED)
478 .with_stroke(Color::rgba8(255, 255, 255, 50), 2.0)
479 .with_opacity(0.0);
480 let sig_lbl = dim("Live Signal demo", 900.0, 150.0);
481
482 for n in [&s7_h, &s7_sub, &s7_arc, &s7_mutex, &s7_why, &sig_lbl] {
483 project.scene.add(Box::new(n.clone()));
484 }
485 project.scene.add(Box::new(s7_code.clone()));
486 project.scene.add(Box::new(s7_diagram_code.clone()));
487 project.scene.add(Box::new(sig_demo.clone()));
488
489 // =====================================================================
490 // S8: SIGNAL TWEEN — How animations work per-frame
491 // =====================================================================
492 let s8_h = title("SignalTween — The Animation Engine", 50.0);
493 let s8_sub = body(
494 ".to() creates a SignalTween that interpolates over time:",
495 90.0,
496 );
497
498 let s8_code = code_block(
499 "pub struct SignalTween<T> {
500 data: Arc<Mutex<SignalData<T>>>, // shared ref to signal
501 start_value: Option<T>, // captured on FIRST update (lazy!)
502 target_value: Option<T>, // where we're going
503 duration: Duration, // how long
504 elapsed: Duration, // how much time passed
505 easing: fn(f32) -> f32, // curve function
506}",
507 125.0,
508 );
509
510 let s8_how = h2("Each frame update:", 340.0);
511 let s8_steps = [
512 "1. elapsed += dt",
513 "2. t_linear = elapsed / duration (0.0 to 1.0)",
514 "3. t_eased = easing(t_linear) (curved)",
515 "4. value = lerp(start, target, t) (interpolate)",
516 "5. Write value into Signal (node sees it)",
517 "6. If elapsed >= duration: finished! (return leftover dt)",
518 ];
519 let s8_step_texts: Vec<TextNode> = s8_steps
520 .iter()
521 .enumerate()
522 .map(|(i, s)| body(s, 370.0 + i as f32 * 28.0))
523 .collect();
524
525 let s8_lazy = note(
526 "start_value is captured lazily — so chained tweens read the",
527 570.0,
528 );
529 let s8_lazy2 = note(
530 "correct value at their actual start time, not creation time.",
531 590.0,
532 );
533
534 // Progress bar
535 let prog_bg = Rect::default()
536 .with_position(Vec2::new(895.0, 150.0))
537 .with_size(Vec2::new(400.0, 16.0))
538 .with_fill(Color::rgba8(255, 255, 255, 15))
539 .with_radius(8.0)
540 .with_opacity(0.0);
541 let prog_fill = Rect::default()
542 .with_position(Vec2::new(695.0, 154.0))
543 .with_anchor(Vec2::new(-1.0, 0.5))
544 .with_size(Vec2::new(0.0, 16.0))
545 .with_fill(ACCENT)
546 .with_radius(8.0)
547 .with_opacity(0.0);
548 let plbl0 = dim("t=0", 700.0, 172.0);
549 let plbl1 = dim("t=1", 1070.0, 172.0);
550 let tween_ball = Circle::default()
551 .with_position(Vec2::new(900.0, 430.0))
552 .with_radius(30.0)
553 .with_fill(RED)
554 .with_opacity(0.0);
555 let tween_lbl = dim("radius animating: 30 -> 80", 760.0, 220.0);
556
557 for n in [
558 &s8_h, &s8_sub, &s8_how, &s8_lazy, &s8_lazy2, &plbl0, &plbl1, &tween_lbl,
559 ] {
560 project.scene.add(Box::new(n.clone()));
561 }
562 project.scene.add(Box::new(s8_code.clone()));
563 for t in &s8_step_texts {
564 project.scene.add(Box::new(t.clone()));
565 }
566 project.scene.add(Box::new(prog_bg.clone()));
567 project.scene.add(Box::new(prog_fill.clone()));
568 project.scene.add(Box::new(tween_ball.clone()));
569
570 // =====================================================================
571 // S9: TWEENABLE + EASINGS
572 // =====================================================================
573 let s9_h = title("Tweenable — What Can Be Animated", 50.0);
574 let s9_code = code_block(
575 "pub trait Tweenable: Clone + Send + Sync {
576 fn interpolate(a: &Self, b: &Self, t: f32) -> Self;
577 fn state_hash(&self) -> u64;
578}
579// Implemented for: f32, Vec2, Color, String, Affine, Vec<Vec2>
580// f32: lerp(a, b, t) = a + (b-a)*t
581// Vec2: lerp x and y independently
582// Color: lerp R,G,B,A channels independently
583// String: snap — returns 'a' until t>=1, then 'b'",
584 95.0,
585 );
586
587 let s9_easing_h = h2("Easing functions curve the linear t:", 310.0);
588 let s9_easing_desc = body("Same distance, same duration — different feel.", 340.0);
589
590 let enames = [
591 "linear",
592 "cubic_in_out",
593 "elastic_out",
594 "bounce_out",
595 "back_out",
596 ];
597 let ecolors = [WHITE, ACCENT, RED, YELLOW, GREEN];
598 let mut eballs: Vec<Circle> = Vec::new();
599 let mut elabels: Vec<TextNode> = Vec::new();
600 for (i, name) in enames.iter().enumerate() {
601 let y = 390.0 + i as f32 * 55.0;
602 let b = Circle::default()
603 .with_position(Vec2::new(250.0, y))
604 .with_radius(12.0)
605 .with_fill(ecolors[i])
606 .with_opacity(0.0);
607 let l = dim(name, LEFT, y - 5.0);
608 project.scene.add(Box::new(b.clone()));
609 project.scene.add(Box::new(l.clone()));
610 eballs.push(b);
611 elabels.push(l);
612 }
613 project.scene.add(Box::new(s9_h.clone()));
614 project.scene.add(Box::new(s9_code.clone()));
615 project.scene.add(Box::new(s9_easing_h.clone()));
616 project.scene.add(Box::new(s9_easing_desc.clone()));
617
618 // =====================================================================
619 // S10: FLOW CONTROLS
620 // =====================================================================
621 let s10_h = title("Flow Controls — Composing Animations", 50.0);
622 let s10_sub = body(
623 "Individual tweens are simple. Power comes from composing them.",
624 90.0,
625 );
626
627 // chain
628 let s10_chain_h = h2("chain![ ] — one after another", 140.0);
629 let chain_d: Vec<Circle> = (0..3)
630 .map(|i| {
631 Circle::default()
632 .with_position(Vec2::new(LEFT + 30.0 + i as f32 * 60.0, 200.0))
633 .with_radius(18.0)
634 .with_fill([RED, ACCENT, YELLOW][i])
635 .with_opacity(0.0)
636 })
637 .collect();
638
639 // all
640 let s10_all_h = h2("all![ ] — all at the same time", 270.0);
641 let all_d: Vec<Circle> = (0..3)
642 .map(|i| {
643 Circle::default()
644 .with_position(Vec2::new(LEFT + 30.0 + i as f32 * 60.0, 330.0))
645 .with_radius(18.0)
646 .with_fill([RED, ACCENT, YELLOW][i])
647 .with_opacity(0.0)
648 })
649 .collect();
650
651 // sequence
652 let s10_seq_h = h2("sequence![ ] — staggered starts", 400.0);
653 let seq_d: Vec<Circle> = (0..3)
654 .map(|i| {
655 Circle::default()
656 .with_position(Vec2::new(LEFT + 30.0 + i as f32 * 60.0, 460.0))
657 .with_radius(18.0)
658 .with_fill([RED, ACCENT, YELLOW][i])
659 .with_opacity(0.0)
660 })
661 .collect();
662
663 let s10_code = code_block(
664 "chain![ a, b, c ] // a then b then c
665all![ a, b, c ] // a + b + c together
666sequence![ 200ms, a, b, c ] // staggered
667delay![ 500ms, a ] // wait then play
668wait(1s) // pause
669any![ a, b ] // race: first wins
670loop_anim![ a, 3 ] // repeat 3 times",
671 510.0,
672 );
673
674 project.scene.add(Box::new(s10_h.clone()));
675 project.scene.add(Box::new(s10_sub.clone()));
676 project.scene.add(Box::new(s10_chain_h.clone()));
677 project.scene.add(Box::new(s10_all_h.clone()));
678 project.scene.add(Box::new(s10_seq_h.clone()));
679 project.scene.add(Box::new(s10_code.clone()));
680 for d in &chain_d {
681 project.scene.add(Box::new(d.clone()));
682 }
683 for d in &all_d {
684 project.scene.add(Box::new(d.clone()));
685 }
686 for d in &seq_d {
687 project.scene.add(Box::new(d.clone()));
688 }
689
690 // =====================================================================
691 // S11: TIMELINE + RENDERING
692 // =====================================================================
693 let s11_h = title("The Timeline — Animation Queue", 50.0);
694 let s11_code = code_block(
695 "pub struct Timeline {
696 pub animations: Vec<Box<dyn Animation>>,
697}
698impl Timeline {
699 fn update(&mut self, mut dt: Duration) {
700 while !self.animations.is_empty() {
701 let (finished, leftover) = self.animations[0].update(dt);
702 if finished {
703 self.animations.remove(0); // pop front
704 dt = leftover; // pass leftover to next!
705 } else { break; }
706 }
707 }
708}",
709 95.0,
710 );
711
712 let s11_leftover = note(
713 "leftover propagation: if A finishes mid-frame, the remaining",
714 390.0,
715 );
716 let s11_leftover2 = note(
717 "dt is immediately given to B. No 'lost frames' at transitions.",
718 415.0,
719 );
720
721 let s11_render_h = h2("Rendering Pipeline (per frame):", 470.0);
722 let s11_steps = [
723 "1. Timeline.update(dt) => SignalTween writes to Signals",
724 "2. Node.render() => reads signals, draws shapes",
725 "3. Vello GPU => compiles scene => wgpu => pixels",
726 "4. state_hash() => seahash for hashing scene state, skip if unchanged",
727 ];
728 let s11_render_texts: Vec<TextNode> = s11_steps
729 .iter()
730 .enumerate()
731 .map(|(i, s)| body(s, 505.0 + i as f32 * 28.0))
732 .collect();
733
734 project.scene.add(Box::new(s11_h.clone()));
735 project.scene.add(Box::new(s11_code.clone()));
736 for n in [&s11_leftover, &s11_leftover2, &s11_render_h] {
737 project.scene.add(Box::new(n.clone()));
738 }
739 for t in &s11_render_texts {
740 project.scene.add(Box::new(t.clone()));
741 }
742
743 // =====================================================================
744 // S12: EVENT LOOP — Why an infinite loop?
745 // =====================================================================
746 let s12_h = title("Why an Infinite Loop? — The Event Loop", 50.0);
747 let s12_sub = body(
748 "GPU rendering requires a persistent event loop (winit + wgpu).",
749 90.0,
750 );
751 let s12_code = code_block(
752 "event_loop.run(|event, elwt| {
753 match event {
754 Resumed => { // GPU surface ready
755 renderer.resume(&window);
756 }
757 AboutToWait => { // run every frame
758 scene.update(dt); // advance animations
759 let hash = scene.state_hash();
760 if hash != last_hash { // dirty?
761 window.request_redraw();
762 }
763 }
764 RedrawRequested => { // GPU draw call
765 renderer.render(&scene, w, h);
766 }
767 }
768});",
769 130.0,
770 );
771 let s12_why = note(
772 "The window stays open because the GPU surface is tied to",
773 490.0,
774 );
775 let s12_why2 = note(
776 "the OS event loop. Without it, the surface is immediately dropped.",
777 510.0,
778 );
779 let s12_hash = body(
780 "state_hash() skips re-rendering unchanged frames (dirty-checking).",
781 550.0,
782 );
783
784 for n in [&s12_h, &s12_sub, &s12_why, &s12_why2, &s12_hash] {
785 project.scene.add(Box::new(n.clone()));
786 }
787 project.scene.add(Box::new(s12_code.clone()));
788
789 // =====================================================================
790 // S13: HEADLESS EXPORT — GPU without a window
791 // =====================================================================
792 let s13_h = title("Headless Export: GPU -> PNG -> FFmpeg", 50.0);
793 let s13_sub = body(
794 "Same GPU rendering, but without a window — output to files.",
795 90.0,
796 );
797 let s13_code = code_block(
798 "pub struct Exporter {
799 texture: wgpu::Texture, // GPU-side image
800 output_buffer: wgpu::Buffer, // CPU-readable copy
801 renderer: Renderer, // Vello
802}
803fn export_frame(&mut self, scene) -> Vec<u8> {
804 scene.render(&mut self.scene); // 1. build shapes
805 renderer.render_to_texture(..); // 2. GPU draws
806 encoder.copy_texture_to_buffer(..); // 3. GPU -> CPU
807 output_buffer.map_async(Read, ..); // 4. read pixels
808 return pixels; // 5. raw RGBA
809}",
810 130.0,
811 );
812 let s13_cache = note(
813 "Cache: state_hash per frame. If unchanged, skip GPU entirely.",
814 420.0,
815 );
816 let s13_ffmpeg = note(
817 "FFmpeg: raw pixels piped to stdin -> libx264 -> .mkv video.",
818 445.0,
819 );
820 let s13_parallel = body(
821 "PNG saving runs on a background thread. Export is pipelined.",
822 485.0,
823 );
824
825 for n in [&s13_h, &s13_sub, &s13_cache, &s13_ffmpeg, &s13_parallel] {
826 project.scene.add(Box::new(n.clone()));
827 }
828 project.scene.add(Box::new(s13_code.clone()));
829
830 // =====================================================================
831 // S14: ENGINE UTILITIES
832 // =====================================================================
833 let s14_h = title("Under the Hood: Utility Modules", 50.0);
834 let s14_sub = body(
835 "Helper systems that power the engine behind the scenes.",
836 90.0,
837 );
838 let s14_code = code_block(
839 "// src/engine/util/
840font_manager.rs // Lazy-loads system fonts via Typst
841 // Global HashMap cache with lazy_static
842
843image_manager.rs // Loads PNG + SVG (via resvg)
844 // Caches decoded images as Arc<Image>
845
846code_tokenizer.rs // Syntax highlighting via Syntect
847 // Parses code -> colored spans for CodeNode
848
849hash.rs // SeaHash: fast, deterministic fingerprints
850 // Position-aware combination
851 // Powers Rayon parallel state hashing
852
853export.rs // FFmpeg pipe: rawvideo -> libx264
854 // Audio merging with filter_complex
855 // Title sanitization for filenames",
856 130.0,
857 );
858 let s14_lazy = note(
859 "lazy_static + Mutex = global singleton, created once, cached forever.",
860 495.0,
861 );
862 let s14_arc = body(
863 "Arc<Image> lets multiple nodes share one decoded image without copies.",
864 530.0,
865 );
866 let s14_hash = note(
867 "Rayon + SeaHash = Deterministic fingerprints across runs & threads.",
868 565.0,
869 );
870
871 for n in [&s14_h, &s14_sub, &s14_lazy, &s14_arc, &s14_hash] {
872 project.scene.add(Box::new(n.clone()));
873 }
874 project.scene.add(Box::new(s14_code.clone()));
875
876 // =====================================================================
877 // S15: FINALE
878 // =====================================================================
879 let fin = TextNode::default()
880 .with_anchor(Vec2::new(-1.0, -1.0))
881 .with_position(Vec2::new(LEFT, 200.0))
882 .with_text("That's how it works!")
883 .with_font_size(48.0)
884 .with_fill(ACCENT)
885 .with_font(FONT)
886 .with_opacity(0.0);
887 let fin_steps = [
888 "1. struct — data container",
889 "2. impl — methods / builder pattern",
890 "3. trait Node — interface contract",
891 "4. Box<dyn Node> — type-erased heap allocation",
892 "5. Signal<T> — Arc<Mutex> shared reactive state",
893 "6. SignalTween — per-frame lerp interpolation",
894 "7. Timeline — sequential queue + leftover dt",
895 "8. Event Loop — winit + wgpu infinite loop",
896 "9. Exporter — headless GPU -> PNG/FFmpeg",
897 "10. Utilities — font/image cache, syntax highlight",
898 ];
899 let fin_texts: Vec<TextNode> = fin_steps
900 .iter()
901 .enumerate()
902 .map(|(i, s)| body(s, 270.0 + i as f32 * 28.0))
903 .collect();
904 let fin_hint = dim("cargo run --example getting_started", LEFT, 570.0);
905
906 project.scene.add(Box::new(fin.clone()));
907 for t in &fin_texts {
908 project.scene.add(Box::new(t.clone()));
909 }
910 project.scene.add(Box::new(fin_hint.clone()));
911
912 // =====================================================================
913 // ANIMATION TIMELINE
914 // =====================================================================
915 // Helper: hide_all takes a vec of opacity signals and fades them out
916 let hide_dur = ms(200);
917
918 project.scene.video_timeline.add(chain![
919 // ── S1: TITLE ──
920 s1_line
921 .end
922 .to(Vec2::new(500.0, 100.0), ms(500))
923 .ease(easings::cubic_out),
924 sequence![
925 ms(120),
926 show(&s1_title, ms(500)),
927 show(&s1_sub, ms(500)),
928 show(&s1_built, ms(500)),
929 show(&s1_logo, ms(600)),
930 show(&s1_desc, ms(500)),
931 show(&s1_desc2, ms(500)),
932 ],
933 wait(secs(5)),
934 all![
935 hide(&s1_title, hide_dur),
936 hide(&s1_sub, hide_dur),
937 hide(&s1_built, hide_dur),
938 hide(&s1_desc, hide_dur),
939 hide(&s1_desc2, hide_dur),
940 hide(&s1_logo, hide_dur),
941 s1_line.end.to(Vec2::new(LEFT, 100.0), hide_dur)
942 ],
943 wait(ms(150)),
944 // ── S2: FIVE STEPS ──
945 show(&s2_h, ms(500)),
946 wait(ms(400)),
947 sequence![
948 ms(250),
949 show(&s2_texts[0], ms(400)),
950 show(&s2_texts[1], ms(400)),
951 show(&s2_texts[2], ms(400)),
952 show(&s2_texts[3], ms(400)),
953 show(&s2_texts[4], ms(400)),
954 ],
955 wait(secs(8)),
956 all![
957 hide(&s2_h, hide_dur),
958 hide(&s2_texts[0], hide_dur),
959 hide(&s2_texts[1], hide_dur),
960 hide(&s2_texts[2], hide_dur),
961 hide(&s2_texts[3], hide_dur),
962 hide(&s2_texts[4], hide_dur)
963 ],
964 wait(ms(150)),
965 // ── S3: STRUCT ──
966 sequence![
967 ms(120),
968 show(&s3_h, ms(500)),
969 show(&s3_explain, ms(400)),
970 show(&s3_analogy, ms(400))
971 ],
972 wait(ms(500)),
973 show(&s3_code, ms(500)),
974 wait(secs(6)),
975 sequence![
976 ms(300),
977 show(&s3_note, ms(400)),
978 show(&s3_note2, ms(400)),
979 show(&s3_note3, ms(400))
980 ],
981 wait(secs(6)),
982 all![
983 hide(&s3_h, hide_dur),
984 hide(&s3_explain, hide_dur),
985 hide(&s3_analogy, hide_dur),
986 hide(&s3_code, hide_dur),
987 hide(&s3_note, hide_dur),
988 hide(&s3_note2, hide_dur),
989 hide(&s3_note3, hide_dur)
990 ],
991 wait(ms(150)),
992 // ── S4: IMPL / BUILDER ──
993 sequence![
994 ms(120),
995 show(&s4_h, ms(500)),
996 show(&s4_explain, ms(400)),
997 show(&s4_analogy, ms(400))
998 ],
999 wait(ms(500)),
1000 show(&s4_code, ms(500)),
1001 wait(secs(7)),
1002 show(&s4_usage, ms(400)),
1003 show(&s4_usage_code, ms(500)),
1004 wait(secs(2)),
1005 show(&s4_note, ms(400)),
1006 wait(secs(5)),
1007 all![
1008 hide(&s4_h, hide_dur),
1009 hide(&s4_explain, hide_dur),
1010 hide(&s4_analogy, hide_dur),
1011 hide(&s4_code, hide_dur),
1012 hide(&s4_usage, hide_dur),
1013 hide(&s4_usage_code, hide_dur),
1014 hide(&s4_note, hide_dur)
1015 ],
1016 wait(ms(150)),
1017 // ── S5: TRAIT / NODE ──
1018 sequence![
1019 ms(120),
1020 show(&s5_h, ms(500)),
1021 show(&s5_explain, ms(400)),
1022 show(&s5_analogy, ms(400))
1023 ],
1024 wait(ms(500)),
1025 show(&s5_code, ms(500)),
1026 wait(secs(6)),
1027 sequence![
1028 ms(300),
1029 show(&s5_r, ms(400)),
1030 show(&s5_u, ms(400)),
1031 show(&s5_s, ms(400)),
1032 show(&s5_c, ms(400))
1033 ],
1034 wait(secs(2)),
1035 show(&s5_every, ms(400)),
1036 wait(secs(4)),
1037 all![
1038 hide(&s5_h, hide_dur),
1039 hide(&s5_explain, hide_dur),
1040 hide(&s5_analogy, hide_dur),
1041 hide(&s5_code, hide_dur),
1042 hide(&s5_r, hide_dur),
1043 hide(&s5_u, hide_dur),
1044 hide(&s5_s, hide_dur),
1045 hide(&s5_c, hide_dur),
1046 hide(&s5_every, hide_dur)
1047 ],
1048 wait(ms(150)),
1049 // ── S6: NODE GALLERY ──
1050 sequence![ms(120), show(&s6_h, ms(500)), show(&s6_sub, ms(400))],
1051 wait(ms(400)),
1052 sequence![
1053 ms(200),
1054 all![show(&demo_c, ms(400)), show(&lc, ms(400))],
1055 all![show(&demo_r, ms(400)), show(&lr, ms(400))],
1056 all![show(&demo_l, ms(400)), show(&ll, ms(400))],
1057 all![show(&demo_p, ms(400)), show(&lp, ms(400))],
1058 all![show(&demo_t, ms(400)), show(<, ms(400))],
1059 ],
1060 wait(secs(2)),
1061 sequence![
1062 ms(200),
1063 show(&s6_box_h, ms(400)),
1064 show(&s6_box1, ms(400)),
1065 show(&s6_box_code, ms(500))
1066 ],
1067 wait(secs(7)),
1068 all![
1069 hide(&s6_h, hide_dur),
1070 hide(&s6_sub, hide_dur),
1071 hide(&demo_c, hide_dur),
1072 hide(&demo_r, hide_dur),
1073 hide(&demo_l, hide_dur),
1074 hide(&demo_p, hide_dur),
1075 hide(&demo_t, hide_dur),
1076 hide(&lc, hide_dur),
1077 hide(&lr, hide_dur),
1078 hide(&ll, hide_dur),
1079 hide(&lp, hide_dur),
1080 hide(<, hide_dur),
1081 hide(&s6_box_h, hide_dur),
1082 hide(&s6_box1, hide_dur),
1083 hide(&s6_box_code, hide_dur)
1084 ],
1085 wait(ms(150)),
1086 // ── S7: SIGNALS ──
1087 sequence![ms(120), show(&s7_h, ms(500)), show(&s7_sub, ms(400))],
1088 wait(ms(400)),
1089 show(&s7_code, ms(500)),
1090 wait(secs(5)),
1091 sequence![ms(300), show(&s7_arc, ms(400)), show(&s7_mutex, ms(400))],
1092 wait(secs(4)),
1093 show(&s7_why, ms(400)),
1094 show(&s7_diagram_code, ms(500)),
1095 wait(secs(6)),
1096 // Live demo
1097 all![show(&sig_demo, ms(300)), show(&sig_lbl, ms(300))],
1098 chain![
1099 sig_demo.radius.to(80.0, ms(700)).ease(easings::elastic_out),
1100 sig_demo.fill_color.to(TEAL, ms(500)),
1101 sig_demo
1102 .position
1103 .to(Vec2::new(950.0, 350.0), ms(500))
1104 .ease(easings::cubic_out),
1105 wait(ms(300)),
1106 all![
1107 sig_demo.radius.to(50.0, ms(400)),
1108 sig_demo.fill_color.to(RED, ms(400)),
1109 sig_demo.position.to(Vec2::new(900.0, 300.0), ms(400))
1110 ],
1111 ],
1112 wait(secs(3)),
1113 all![
1114 hide(&s7_h, hide_dur),
1115 hide(&s7_sub, hide_dur),
1116 hide(&s7_code, hide_dur),
1117 hide(&s7_arc, hide_dur),
1118 hide(&s7_mutex, hide_dur),
1119 hide(&s7_why, hide_dur),
1120 hide(&s7_diagram_code, hide_dur),
1121 hide(&sig_demo, hide_dur),
1122 hide(&sig_lbl, hide_dur)
1123 ],
1124 wait(ms(150)),
1125 // ── S8: SIGNAL TWEEN ──
1126 sequence![ms(120), show(&s8_h, ms(500)), show(&s8_sub, ms(400))],
1127 wait(ms(400)),
1128 show(&s8_code, ms(500)),
1129 wait(secs(6)),
1130 show(&s8_how, ms(300)),
1131 sequence![
1132 ms(100),
1133 show(&s8_step_texts[0], ms(250)),
1134 show(&s8_step_texts[1], ms(250)),
1135 show(&s8_step_texts[2], ms(250)),
1136 show(&s8_step_texts[3], ms(250)),
1137 show(&s8_step_texts[4], ms(250)),
1138 show(&s8_step_texts[5], ms(250)),
1139 ],
1140 wait(ms(500)),
1141 sequence![ms(100), show(&s8_lazy, ms(300)), show(&s8_lazy2, ms(300))],
1142 wait(ms(500)),
1143 // Progress bar demo
1144 all![
1145 show(&prog_bg, ms(200)),
1146 show(&prog_fill, ms(200)),
1147 show(&plbl0, ms(200)),
1148 show(&plbl1, ms(200)),
1149 show(&tween_ball, ms(200)),
1150 show(&tween_lbl, ms(200))
1151 ],
1152 all![
1153 prog_fill
1154 .size
1155 .to(Vec2::new(400.0, 16.0), secs(2))
1156 .ease(easings::cubic_in_out),
1157 tween_ball
1158 .radius
1159 .to(80.0, secs(2))
1160 .ease(easings::cubic_in_out),
1161 ],
1162 wait(secs(3)),
1163 all![
1164 hide(&s8_h, hide_dur),
1165 hide(&s8_sub, hide_dur),
1166 hide(&s8_code, hide_dur),
1167 hide(&s8_how, hide_dur),
1168 hide(&s8_lazy, hide_dur),
1169 hide(&s8_lazy2, hide_dur),
1170 hide(&prog_bg, hide_dur),
1171 hide(&prog_fill, hide_dur),
1172 hide(&plbl0, hide_dur),
1173 hide(&plbl1, hide_dur),
1174 hide(&tween_ball, hide_dur),
1175 hide(&tween_lbl, hide_dur),
1176 hide(&s8_step_texts[0], hide_dur),
1177 hide(&s8_step_texts[1], hide_dur),
1178 hide(&s8_step_texts[2], hide_dur),
1179 hide(&s8_step_texts[3], hide_dur),
1180 hide(&s8_step_texts[4], hide_dur),
1181 hide(&s8_step_texts[5], hide_dur)
1182 ],
1183 wait(ms(150)),
1184 // ── S9: TWEENABLE + EASINGS ──
1185 show(&s9_h, ms(500)),
1186 show(&s9_code, ms(500)),
1187 wait(secs(4)),
1188 sequence![
1189 ms(60),
1190 show(&s9_easing_h, ms(300)),
1191 show(&s9_easing_desc, ms(300))
1192 ],
1193 sequence![
1194 ms(50),
1195 all![show(&eballs[0], ms(200)), show(&elabels[0], ms(200))],
1196 all![show(&eballs[1], ms(200)), show(&elabels[1], ms(200))],
1197 all![show(&eballs[2], ms(200)), show(&elabels[2], ms(200))],
1198 all![show(&eballs[3], ms(200)), show(&elabels[3], ms(200))],
1199 all![show(&eballs[4], ms(200)), show(&elabels[4], ms(200))],
1200 ],
1201 wait(ms(300)),
1202 // Race!
1203 all![
1204 eballs[0]
1205 .position
1206 .to(Vec2::new(1050.0, 390.0), secs(2))
1207 .ease(easings::linear),
1208 eballs[1]
1209 .position
1210 .to(Vec2::new(1050.0, 445.0), secs(2))
1211 .ease(easings::cubic_in_out),
1212 eballs[2]
1213 .position
1214 .to(Vec2::new(1050.0, 500.0), secs(2))
1215 .ease(easings::elastic_out),
1216 eballs[3]
1217 .position
1218 .to(Vec2::new(1050.0, 555.0), secs(2))
1219 .ease(easings::bounce_out),
1220 eballs[4]
1221 .position
1222 .to(Vec2::new(1050.0, 610.0), secs(2))
1223 .ease(easings::back_out),
1224 ],
1225 wait(ms(500)),
1226 all![
1227 eballs[0]
1228 .position
1229 .to(Vec2::new(250.0, 390.0), secs(2))
1230 .ease(easings::linear),
1231 eballs[1]
1232 .position
1233 .to(Vec2::new(250.0, 445.0), secs(2))
1234 .ease(easings::cubic_in_out),
1235 eballs[2]
1236 .position
1237 .to(Vec2::new(250.0, 500.0), secs(2))
1238 .ease(easings::elastic_out),
1239 eballs[3]
1240 .position
1241 .to(Vec2::new(250.0, 555.0), secs(2))
1242 .ease(easings::bounce_out),
1243 eballs[4]
1244 .position
1245 .to(Vec2::new(250.0, 610.0), secs(2))
1246 .ease(easings::back_out),
1247 ],
1248 wait(ms(500)),
1249 all![
1250 hide(&s9_h, hide_dur),
1251 hide(&s9_code, hide_dur),
1252 hide(&s9_easing_h, hide_dur),
1253 hide(&s9_easing_desc, hide_dur),
1254 hide(&eballs[0], hide_dur),
1255 hide(&eballs[1], hide_dur),
1256 hide(&eballs[2], hide_dur),
1257 hide(&eballs[3], hide_dur),
1258 hide(&eballs[4], hide_dur),
1259 hide(&elabels[0], hide_dur),
1260 hide(&elabels[1], hide_dur),
1261 hide(&elabels[2], hide_dur),
1262 hide(&elabels[3], hide_dur),
1263 hide(&elabels[4], hide_dur)
1264 ],
1265 wait(ms(150)),
1266 // ── S10: FLOW CONTROLS ──
1267 sequence![ms(120), show(&s10_h, ms(500)), show(&s10_sub, ms(400))],
1268 wait(ms(400)),
1269 // chain demo
1270 show(&s10_chain_h, ms(400)),
1271 all![
1272 show(&chain_d[0], ms(300)),
1273 show(&chain_d[1], ms(300)),
1274 show(&chain_d[2], ms(300))
1275 ],
1276 wait(ms(300)),
1277 chain![
1278 chain_d[0]
1279 .position
1280 .to(Vec2::new(700.0, 200.0), ms(500))
1281 .ease(easings::cubic_out),
1282 chain_d[1]
1283 .position
1284 .to(Vec2::new(800.0, 200.0), ms(500))
1285 .ease(easings::cubic_out),
1286 chain_d[2]
1287 .position
1288 .to(Vec2::new(900.0, 200.0), ms(500))
1289 .ease(easings::cubic_out),
1290 ],
1291 wait(secs(1)),
1292 // all demo
1293 show(&s10_all_h, ms(400)),
1294 all![
1295 show(&all_d[0], ms(300)),
1296 show(&all_d[1], ms(300)),
1297 show(&all_d[2], ms(300))
1298 ],
1299 wait(ms(300)),
1300 all![
1301 all_d[0]
1302 .position
1303 .to(Vec2::new(700.0, 330.0), ms(500))
1304 .ease(easings::cubic_out),
1305 all_d[1]
1306 .position
1307 .to(Vec2::new(800.0, 330.0), ms(500))
1308 .ease(easings::cubic_out),
1309 all_d[2]
1310 .position
1311 .to(Vec2::new(900.0, 330.0), ms(500))
1312 .ease(easings::cubic_out),
1313 ],
1314 wait(secs(1)),
1315 // sequence demo
1316 show(&s10_seq_h, ms(400)),
1317 all![
1318 show(&seq_d[0], ms(300)),
1319 show(&seq_d[1], ms(300)),
1320 show(&seq_d[2], ms(300))
1321 ],
1322 wait(ms(300)),
1323 sequence![
1324 ms(250),
1325 seq_d[0]
1326 .position
1327 .to(Vec2::new(700.0, 460.0), ms(500))
1328 .ease(easings::cubic_out),
1329 seq_d[1]
1330 .position
1331 .to(Vec2::new(800.0, 460.0), ms(500))
1332 .ease(easings::cubic_out),
1333 seq_d[2]
1334 .position
1335 .to(Vec2::new(900.0, 460.0), ms(500))
1336 .ease(easings::cubic_out),
1337 ],
1338 wait(secs(1)),
1339 show(&s10_code, ms(500)),
1340 wait(secs(8)),
1341 all![
1342 hide(&s10_h, hide_dur),
1343 hide(&s10_sub, hide_dur),
1344 hide(&s10_chain_h, hide_dur),
1345 hide(&s10_all_h, hide_dur),
1346 hide(&s10_seq_h, hide_dur),
1347 hide(&s10_code, hide_dur),
1348 hide(&chain_d[0], hide_dur),
1349 hide(&chain_d[1], hide_dur),
1350 hide(&chain_d[2], hide_dur),
1351 hide(&all_d[0], hide_dur),
1352 hide(&all_d[1], hide_dur),
1353 hide(&all_d[2], hide_dur),
1354 hide(&seq_d[0], hide_dur),
1355 hide(&seq_d[1], hide_dur),
1356 hide(&seq_d[2], hide_dur)
1357 ],
1358 wait(ms(150)),
1359 // ── S11: TIMELINE + RENDERING ──
1360 show(&s11_h, ms(500)),
1361 wait(ms(400)),
1362 show(&s11_code, ms(500)),
1363 wait(secs(7)),
1364 sequence![
1365 ms(200),
1366 show(&s11_leftover, ms(400)),
1367 show(&s11_leftover2, ms(400))
1368 ],
1369 wait(secs(4)),
1370 show(&s11_render_h, ms(400)),
1371 sequence![
1372 ms(200),
1373 show(&s11_render_texts[0], ms(350)),
1374 show(&s11_render_texts[1], ms(350)),
1375 show(&s11_render_texts[2], ms(350)),
1376 show(&s11_render_texts[3], ms(350)),
1377 ],
1378 wait(secs(7)),
1379 all![
1380 hide(&s11_h, hide_dur),
1381 hide(&s11_code, hide_dur),
1382 hide(&s11_leftover, hide_dur),
1383 hide(&s11_leftover2, hide_dur),
1384 hide(&s11_render_h, hide_dur),
1385 hide(&s11_render_texts[0], hide_dur),
1386 hide(&s11_render_texts[1], hide_dur),
1387 hide(&s11_render_texts[2], hide_dur),
1388 hide(&s11_render_texts[3], hide_dur)
1389 ],
1390 wait(ms(300)),
1391 // ── S12: EVENT LOOP ──
1392 sequence![ms(120), show(&s12_h, ms(500)), show(&s12_sub, ms(400))],
1393 wait(ms(500)),
1394 show(&s12_code, ms(500)),
1395 wait(secs(8)),
1396 sequence![
1397 ms(200),
1398 show(&s12_why, ms(400)),
1399 show(&s12_why2, ms(400)),
1400 show(&s12_hash, ms(400))
1401 ],
1402 wait(secs(5)),
1403 all![
1404 hide(&s12_h, hide_dur),
1405 hide(&s12_sub, hide_dur),
1406 hide(&s12_code, hide_dur),
1407 hide(&s12_why, hide_dur),
1408 hide(&s12_why2, hide_dur),
1409 hide(&s12_hash, hide_dur)
1410 ],
1411 wait(ms(150)),
1412 // ── S13: HEADLESS EXPORT ──
1413 sequence![ms(120), show(&s13_h, ms(500)), show(&s13_sub, ms(400))],
1414 wait(ms(500)),
1415 show(&s13_code, ms(500)),
1416 wait(secs(8)),
1417 sequence![
1418 ms(200),
1419 show(&s13_cache, ms(400)),
1420 show(&s13_ffmpeg, ms(400)),
1421 show(&s13_parallel, ms(400))
1422 ],
1423 wait(secs(5)),
1424 all![
1425 hide(&s13_h, hide_dur),
1426 hide(&s13_sub, hide_dur),
1427 hide(&s13_code, hide_dur),
1428 hide(&s13_cache, hide_dur),
1429 hide(&s13_ffmpeg, hide_dur),
1430 hide(&s13_parallel, hide_dur)
1431 ],
1432 wait(ms(150)),
1433 // ── S14: UTILITIES ──
1434 sequence![ms(120), show(&s14_h, ms(500)), show(&s14_sub, ms(400))],
1435 wait(ms(500)),
1436 show(&s14_code, ms(500)),
1437 wait(secs(8)),
1438 sequence![ms(200), show(&s14_lazy, ms(400)), show(&s14_arc, ms(400))],
1439 wait(secs(5)),
1440 all![
1441 hide(&s14_h, hide_dur),
1442 hide(&s14_sub, hide_dur),
1443 hide(&s14_code, hide_dur),
1444 hide(&s14_lazy, hide_dur),
1445 hide(&s14_arc, hide_dur)
1446 ],
1447 wait(ms(300)),
1448 // ── S15: FINALE ──
1449 show(&fin, ms(700)),
1450 wait(ms(500)),
1451 sequence![
1452 ms(150),
1453 show(&fin_texts[0], ms(350)),
1454 show(&fin_texts[1], ms(350)),
1455 show(&fin_texts[2], ms(350)),
1456 show(&fin_texts[3], ms(350)),
1457 show(&fin_texts[4], ms(350)),
1458 show(&fin_texts[5], ms(350)),
1459 show(&fin_texts[6], ms(350)),
1460 show(&fin_texts[7], ms(350)),
1461 show(&fin_texts[8], ms(350)),
1462 show(&fin_texts[9], ms(350)),
1463 ],
1464 wait(secs(2)),
1465 show(&fin_hint, ms(400)),
1466 wait(secs(6)),
1467 ]);
1468
1469 #[cfg(feature = "audio")]
1470 project
1471 .scene
1472 .audio_timeline
1473 .add(play!(AudioNode::new("background.mp3").with_volume(0.3)));
1474
1475 project.show().expect("Failed to render");
1476}pub fn with_close_on_finish(self, close: bool) -> Self
Sourcepub fn close_on_finish(self) -> Self
pub fn close_on_finish(self) -> Self
Examples found in repository?
examples/color_interpolation.rs (line 10)
4fn main() {
5 let mut project = Project::default()
6 .with_dimensions(300, 300)
7 .with_fps(60)
8 .with_title("Color Interpolation")
9 .with_cache(true)
10 .close_on_finish();
11
12 let circle = Circle::default()
13 .with_position(Vec2::new(150.0, 150.0))
14 .with_radius(50.0)
15 .with_fill(Color::RED); // Red
16
17 project.scene.add(Box::new(circle.clone()));
18
19 let duration = Duration::from_secs(1);
20
21 project.scene.video_timeline.add(loop_anim(
22 move || {
23 chain![
24 circle.fill_color.to(Color::YELLOW, duration),
25 circle.fill_color.to(Color::GREEN, duration),
26 circle.fill_color.to(Color::BLUE, duration),
27 circle.fill_color.to(Color::RED, duration),
28 ]
29 },
30 None,
31 ));
32
33 // Show
34 project.show().expect("Failed to render");
35}More examples
examples/math_animation.rs (line 10)
4fn main() {
5 // 1. Initialize Project
6 let mut project = Project::default()
7 .with_fps(60)
8 .with_dimensions(500, 500)
9 .with_title("Math Animation")
10 .close_on_finish();
11
12 // 2. Create MathNode (Typst syntax)
13 let tex = MathNode::default()
14 .with_position(Vec2::new(250.0, 250.0))
15 .with_equation("y = a x^2")
16 .with_font_size(48.0)
17 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2));
18 project.scene.add(Box::new(tex.clone()));
19
20 // 3. Define Animation Sequence
21 project.scene.video_timeline.add(loop_anim(
22 move || {
23 chain![
24 tex.tex("y = a x^2 + b x", Duration::from_secs(1)),
25 tex.tex("e^(i pi) + 1 = 0", Duration::from_secs(1)),
26 tex.tex("y = a x^2", Duration::from_secs(1)),
27 ]
28 },
29 None,
30 ));
31
32 // 4. Run interactive preview
33 project.show().expect("Failed to render");
34}examples/getting_started.rs (line 10)
4fn main() {
5 // 1. Initialize the Project
6 let mut project = Project::default()
7 .with_fps(60)
8 .with_cache(true)
9 .with_title("Getting Started")
10 .close_on_finish();
11
12 // 2. Define Nodes
13 let circle = Circle::default()
14 .with_position(Vec2::new(400.0, 300.0))
15 .with_radius(50.0)
16 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
17
18 let text = TextNode::default()
19 .with_position(Vec2::new(400.0, 450.0))
20 .with_text("Hello Rust")
21 .with_font_size(40.0)
22 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White-ish
23
24 // 3. Add Nodes to the Scene
25 project.scene.add(Box::new(circle.clone()));
26 project.scene.add(Box::new(text.clone()));
27
28 // 4. Add Animations to the Timeline
29 project.scene.video_timeline.add(all![
30 circle.radius.to(100.0, Duration::from_secs(1)),
31 text.position
32 .to(Vec2::new(400.0, 500.0), Duration::from_secs(1)),
33 ]);
34
35 // 5. Show
36 project.show().expect("Failed to render");
37}examples/code_advanced.rs (line 7)
4fn main() {
5 let mut project = Project::default()
6 .with_title("Code Advanced")
7 .close_on_finish();
8
9 let code = CodeNode::default()
10 .with_position(Vec2::new(50.0, 50.0))
11 .with_code(
12 r#"fn main() {
13 println!("Hello");
14}"#,
15 )
16 .with_language("rust")
17 .with_font_size(32.0)
18 .with_dim_opacity(0.1);
19
20 project.scene.add(Box::new(code.clone()));
21
22 project.scene.video_timeline.add(sequence![
23 Duration::from_secs(1),
24 // 1. Select line 2 (println) - using 1-based index string
25 code.select_string("2", Duration::from_millis(300)),
26 // 2. Select range 1-2
27 code.select_string("1-2", Duration::from_millis(300)),
28 // 3. Append a comment
29 code.append("\n// Done!", Duration::from_millis(300)),
30 // 3. Reset selection
31 code.select_lines(vec![], Duration::from_millis(300)),
32 // 4. Prepend a header (Now natively lazy, no wrapper needed!)
33 code.prepend("// My Script\n", Duration::from_millis(300)),
34 ]);
35
36 project.show().expect("Failed to render");
37}examples/export.rs (line 11)
4fn main() {
5 // 1. Initialize for Export
6 let mut project = Project::default()
7 .with_fps(30)
8 .with_ffmpeg(true)
9 .with_title("Export")
10 .with_output_path("output")
11 .close_on_finish();
12
13 // 2. Setup Nodes
14 let circle = Circle::default()
15 .with_position(Vec2::new(400.0, 300.0))
16 .with_radius(50.0)
17 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)); // Blue
18
19 let text = TextNode::default()
20 .with_position(Vec2::new(400.0, 50.0))
21 .with_text("Export Demo")
22 .with_font_size(40.0)
23 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White
24
25 project.scene.add(Box::new(circle.clone()));
26 project.scene.add(Box::new(text.clone()));
27
28 // 3. Define Animations (Color and Font Size)
29 project.scene.video_timeline.add(all![
30 // Circle color and size
31 circle
32 .fill_color
33 .to(Color::rgb8(0xf2, 0xf2, 0xf2), Duration::from_secs(2))
34 .ease(easings::quad_in_out),
35 circle
36 .radius
37 .to(150.0, Duration::from_secs(2))
38 .ease(easings::elastic_out),
39 // Text font size
40 text.font_size
41 .to(50.0, Duration::from_secs(2))
42 .ease(easings::cubic_out),
43 ]);
44
45 // 4. Export (Renders frames and combines them into out.mkv)
46 println!("Starting export to {}...", project.output_path.display());
47 project.export().expect("Failed to export");
48}examples/polygon.rs (line 5)
4fn main() {
5 let mut project = Project::default().with_title("Polygon").close_on_finish();
6
7 // Create a regular pentagon
8 let pentagon = Polygon::regular(5, 100.0)
9 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)) // Red
10 .with_position(Vec2::new(200.0, 300.0))
11 .with_scale(0.0);
12
13 // Create a custom triangle
14 let triangle = Polygon::default()
15 .with_position(Vec2::new(500.0, 300.0))
16 .with_points(vec![
17 Vec2::new(0.0, -100.0),
18 Vec2::new(100.0, 100.0),
19 Vec2::new(-100.0, 100.0),
20 ])
21 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
22 .with_stroke(Color::WHITE, 4.0);
23
24 project.scene.add(Box::new(pentagon.clone()));
25 project.scene.add(Box::new(triangle.clone()));
26
27 // Animate rotation and opacity
28 project.scene.video_timeline.add(all![
29 chain![
30 pentagon.scale.to(Vec2::ONE, Duration::from_secs(1)),
31 pentagon
32 .rotation
33 .to(std::f32::consts::PI, Duration::from_secs(2)),
34 pentagon.scale.to(-Vec2::ONE, Duration::from_secs(1)),
35 ],
36 chain![
37 triangle.opacity.to(1.0, Duration::from_secs(1)),
38 triangle
39 .position
40 .to(Vec2::new(500.0, 300.0), Duration::from_secs(1)),
41 triangle
42 .rotation
43 .to(360.0_f32.to_radians(), Duration::from_secs(1))
44 ],
45 ]);
46
47 project.show().expect("Failed to render");
48}Additional examples can be found in:
- examples/audio_demo.rs
- examples/images.rs
- examples/group_animation.rs
- examples/grid.rs
- examples/camera_demo.rs
- examples/code_animation.rs
- examples/nested_cameras.rs
- examples/math_code.rs
- examples/easing_scope.rs
- examples/signals.rs
- examples/advanced_flow.rs
- examples/anchors.rs
- examples/news_feed.rs
- examples/explainer.rs
Sourcepub fn export(&mut self) -> Result<()>
pub fn export(&mut self) -> Result<()>
Examples found in repository?
examples/export.rs (line 47)
4fn main() {
5 // 1. Initialize for Export
6 let mut project = Project::default()
7 .with_fps(30)
8 .with_ffmpeg(true)
9 .with_title("Export")
10 .with_output_path("output")
11 .close_on_finish();
12
13 // 2. Setup Nodes
14 let circle = Circle::default()
15 .with_position(Vec2::new(400.0, 300.0))
16 .with_radius(50.0)
17 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)); // Blue
18
19 let text = TextNode::default()
20 .with_position(Vec2::new(400.0, 50.0))
21 .with_text("Export Demo")
22 .with_font_size(40.0)
23 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White
24
25 project.scene.add(Box::new(circle.clone()));
26 project.scene.add(Box::new(text.clone()));
27
28 // 3. Define Animations (Color and Font Size)
29 project.scene.video_timeline.add(all![
30 // Circle color and size
31 circle
32 .fill_color
33 .to(Color::rgb8(0xf2, 0xf2, 0xf2), Duration::from_secs(2))
34 .ease(easings::quad_in_out),
35 circle
36 .radius
37 .to(150.0, Duration::from_secs(2))
38 .ease(easings::elastic_out),
39 // Text font size
40 text.font_size
41 .to(50.0, Duration::from_secs(2))
42 .ease(easings::cubic_out),
43 ]);
44
45 // 4. Export (Renders frames and combines them into out.mkv)
46 println!("Starting export to {}...", project.output_path.display());
47 project.export().expect("Failed to export");
48}Sourcepub fn show(self) -> Result<()>
pub fn show(self) -> Result<()>
Examples found in repository?
examples/color_interpolation.rs (line 34)
4fn main() {
5 let mut project = Project::default()
6 .with_dimensions(300, 300)
7 .with_fps(60)
8 .with_title("Color Interpolation")
9 .with_cache(true)
10 .close_on_finish();
11
12 let circle = Circle::default()
13 .with_position(Vec2::new(150.0, 150.0))
14 .with_radius(50.0)
15 .with_fill(Color::RED); // Red
16
17 project.scene.add(Box::new(circle.clone()));
18
19 let duration = Duration::from_secs(1);
20
21 project.scene.video_timeline.add(loop_anim(
22 move || {
23 chain![
24 circle.fill_color.to(Color::YELLOW, duration),
25 circle.fill_color.to(Color::GREEN, duration),
26 circle.fill_color.to(Color::BLUE, duration),
27 circle.fill_color.to(Color::RED, duration),
28 ]
29 },
30 None,
31 ));
32
33 // Show
34 project.show().expect("Failed to render");
35}More examples
examples/math_animation.rs (line 33)
4fn main() {
5 // 1. Initialize Project
6 let mut project = Project::default()
7 .with_fps(60)
8 .with_dimensions(500, 500)
9 .with_title("Math Animation")
10 .close_on_finish();
11
12 // 2. Create MathNode (Typst syntax)
13 let tex = MathNode::default()
14 .with_position(Vec2::new(250.0, 250.0))
15 .with_equation("y = a x^2")
16 .with_font_size(48.0)
17 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2));
18 project.scene.add(Box::new(tex.clone()));
19
20 // 3. Define Animation Sequence
21 project.scene.video_timeline.add(loop_anim(
22 move || {
23 chain![
24 tex.tex("y = a x^2 + b x", Duration::from_secs(1)),
25 tex.tex("e^(i pi) + 1 = 0", Duration::from_secs(1)),
26 tex.tex("y = a x^2", Duration::from_secs(1)),
27 ]
28 },
29 None,
30 ));
31
32 // 4. Run interactive preview
33 project.show().expect("Failed to render");
34}examples/getting_started.rs (line 36)
4fn main() {
5 // 1. Initialize the Project
6 let mut project = Project::default()
7 .with_fps(60)
8 .with_cache(true)
9 .with_title("Getting Started")
10 .close_on_finish();
11
12 // 2. Define Nodes
13 let circle = Circle::default()
14 .with_position(Vec2::new(400.0, 300.0))
15 .with_radius(50.0)
16 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
17
18 let text = TextNode::default()
19 .with_position(Vec2::new(400.0, 450.0))
20 .with_text("Hello Rust")
21 .with_font_size(40.0)
22 .with_fill(Color::rgb8(0xf2, 0xf2, 0xf2)); // White-ish
23
24 // 3. Add Nodes to the Scene
25 project.scene.add(Box::new(circle.clone()));
26 project.scene.add(Box::new(text.clone()));
27
28 // 4. Add Animations to the Timeline
29 project.scene.video_timeline.add(all![
30 circle.radius.to(100.0, Duration::from_secs(1)),
31 text.position
32 .to(Vec2::new(400.0, 500.0), Duration::from_secs(1)),
33 ]);
34
35 // 5. Show
36 project.show().expect("Failed to render");
37}examples/code_advanced.rs (line 36)
4fn main() {
5 let mut project = Project::default()
6 .with_title("Code Advanced")
7 .close_on_finish();
8
9 let code = CodeNode::default()
10 .with_position(Vec2::new(50.0, 50.0))
11 .with_code(
12 r#"fn main() {
13 println!("Hello");
14}"#,
15 )
16 .with_language("rust")
17 .with_font_size(32.0)
18 .with_dim_opacity(0.1);
19
20 project.scene.add(Box::new(code.clone()));
21
22 project.scene.video_timeline.add(sequence![
23 Duration::from_secs(1),
24 // 1. Select line 2 (println) - using 1-based index string
25 code.select_string("2", Duration::from_millis(300)),
26 // 2. Select range 1-2
27 code.select_string("1-2", Duration::from_millis(300)),
28 // 3. Append a comment
29 code.append("\n// Done!", Duration::from_millis(300)),
30 // 3. Reset selection
31 code.select_lines(vec![], Duration::from_millis(300)),
32 // 4. Prepend a header (Now natively lazy, no wrapper needed!)
33 code.prepend("// My Script\n", Duration::from_millis(300)),
34 ]);
35
36 project.show().expect("Failed to render");
37}examples/polygon.rs (line 47)
4fn main() {
5 let mut project = Project::default().with_title("Polygon").close_on_finish();
6
7 // Create a regular pentagon
8 let pentagon = Polygon::regular(5, 100.0)
9 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)) // Red
10 .with_position(Vec2::new(200.0, 300.0))
11 .with_scale(0.0);
12
13 // Create a custom triangle
14 let triangle = Polygon::default()
15 .with_position(Vec2::new(500.0, 300.0))
16 .with_points(vec![
17 Vec2::new(0.0, -100.0),
18 Vec2::new(100.0, 100.0),
19 Vec2::new(-100.0, 100.0),
20 ])
21 .with_fill(Color::rgb8(0x68, 0xab, 0xdf)) // Blue
22 .with_stroke(Color::WHITE, 4.0);
23
24 project.scene.add(Box::new(pentagon.clone()));
25 project.scene.add(Box::new(triangle.clone()));
26
27 // Animate rotation and opacity
28 project.scene.video_timeline.add(all![
29 chain![
30 pentagon.scale.to(Vec2::ONE, Duration::from_secs(1)),
31 pentagon
32 .rotation
33 .to(std::f32::consts::PI, Duration::from_secs(2)),
34 pentagon.scale.to(-Vec2::ONE, Duration::from_secs(1)),
35 ],
36 chain![
37 triangle.opacity.to(1.0, Duration::from_secs(1)),
38 triangle
39 .position
40 .to(Vec2::new(500.0, 300.0), Duration::from_secs(1)),
41 triangle
42 .rotation
43 .to(360.0_f32.to_radians(), Duration::from_secs(1))
44 ],
45 ]);
46
47 project.show().expect("Failed to render");
48}examples/audio_demo.rs (line 48)
4fn main() {
5 let mut project = Project::default()
6 .with_dimensions(800, 450)
7 .with_title("Audio Demo")
8 .with_background(Color::rgb8(20, 20, 25))
9 .close_on_finish();
10
11 // Setup Video Timeline
12 let rect = Rect::new(Vec2::new(100.0, 100.0), Vec2::new(200.0, 200.0), Color::RED)
13 .with_anchor(Vec2::new(-1.0, -1.0));
14
15 project.scene.add(Box::new(rect.clone()));
16
17 project.scene.video_timeline.add(chain!(
18 all![
19 rect.position
20 .to(Vec2::new(150.0, 150.0), Duration::from_secs(1)),
21 rect.size.to(Vec2::new(200.0, 50.0), Duration::from_secs(1)),
22 rect.fill_color.to(Color::BLUE, Duration::from_secs(1)),
23 ],
24 all![
25 rect.position
26 .to(Vec2::new(350.0, 150.0), Duration::from_secs(1)),
27 rect.fill_color.to(Color::RED, Duration::from_secs(1)),
28 ]
29 ));
30
31 // Setup Audio Timeline using new macros and builder
32 project.scene.audio_timeline.add(chain!(
33 play!(AudioNode::new("./examples/audios/combo-1.mp3").with_volume(0.5)),
34 audio_wait!(0.05),
35 play!(AudioNode::new("./examples/audios/combo-2.mp3").with_volume(1.0))
36 ));
37
38 println!("Project configured with separate video and audio timelines.");
39 println!(
40 "Video duration: {:?}",
41 project.scene.video_timeline.duration()
42 );
43 println!(
44 "Audio duration: {:?}",
45 project.scene.audio_timeline.duration()
46 );
47
48 project.show().expect("Failed to run audio demo");
49}Additional examples can be found in:
- examples/images.rs
- examples/group_animation.rs
- examples/grid.rs
- examples/shapes.rs
- examples/camera_demo.rs
- examples/code_animation.rs
- examples/nested_cameras.rs
- examples/math_code.rs
- examples/easing_scope.rs
- examples/signals.rs
- examples/advanced_flow.rs
- examples/anchors.rs
- examples/news_feed.rs
- examples/explainer.rs
pub fn seek_to(&mut self, target_time: Duration)
pub fn get_frame_name(&self, frame_count: u32) -> String
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Project
impl !RefUnwindSafe for Project
impl Send for Project
impl Sync for Project
impl Unpin for Project
impl UnsafeUnpin for Project
impl !UnwindSafe for Project
Blanket Implementations§
Source§impl<S, D, Swp, Dwp, T> AdaptInto<D, Swp, Dwp, T> for Swhere
T: Real + Zero + Arithmetics + Clone,
Swp: WhitePoint<T>,
Dwp: WhitePoint<T>,
D: AdaptFrom<S, Swp, Dwp, T>,
impl<S, D, Swp, Dwp, T> AdaptInto<D, Swp, Dwp, T> for Swhere
T: Real + Zero + Arithmetics + Clone,
Swp: WhitePoint<T>,
Dwp: WhitePoint<T>,
D: AdaptFrom<S, Swp, Dwp, T>,
Source§fn adapt_into_using<M>(self, method: M) -> Dwhere
M: TransformMatrix<T>,
fn adapt_into_using<M>(self, method: M) -> Dwhere
M: TransformMatrix<T>,
Convert the source color to the destination color using the specified
method.
Source§fn adapt_into(self) -> D
fn adapt_into(self) -> D
Convert the source color to the destination color using the bradford
method by default.
Source§impl<T, C> ArraysFrom<C> for Twhere
C: IntoArrays<T>,
impl<T, C> ArraysFrom<C> for Twhere
C: IntoArrays<T>,
Source§fn arrays_from(colors: C) -> T
fn arrays_from(colors: C) -> T
Cast a collection of colors into a collection of arrays.
Source§impl<T, C> ArraysInto<C> for Twhere
C: FromArrays<T>,
impl<T, C> ArraysInto<C> for Twhere
C: FromArrays<T>,
Source§fn arrays_into(self) -> C
fn arrays_into(self) -> C
Cast this collection of arrays into a collection of colors.
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<WpParam, T, U> Cam16IntoUnclamped<WpParam, T> for Uwhere
T: FromCam16Unclamped<WpParam, U>,
impl<WpParam, T, U> Cam16IntoUnclamped<WpParam, T> for Uwhere
T: FromCam16Unclamped<WpParam, U>,
Source§type Scalar = <T as FromCam16Unclamped<WpParam, U>>::Scalar
type Scalar = <T as FromCam16Unclamped<WpParam, U>>::Scalar
The number type that’s used in
parameters when converting.Source§fn cam16_into_unclamped(
self,
parameters: BakedParameters<WpParam, <U as Cam16IntoUnclamped<WpParam, T>>::Scalar>,
) -> T
fn cam16_into_unclamped( self, parameters: BakedParameters<WpParam, <U as Cam16IntoUnclamped<WpParam, T>>::Scalar>, ) -> T
Converts
self into C, using the provided parameters.Source§impl<T> CheckedAs for T
impl<T> CheckedAs for T
Source§fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
Casts the value.
Source§impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
Source§fn checked_cast_from(src: Src) -> Option<Dst>
fn checked_cast_from(src: Src) -> Option<Dst>
Casts the value.
Source§impl<T, C> ComponentsFrom<C> for Twhere
C: IntoComponents<T>,
impl<T, C> ComponentsFrom<C> for Twhere
C: IntoComponents<T>,
Source§fn components_from(colors: C) -> T
fn components_from(colors: C) -> T
Cast a collection of colors into a collection of color components.
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> Filterable for T
impl<T> Filterable for T
Source§fn filterable(
self,
filter_name: &'static str,
) -> RequestFilterDataProvider<T, fn(DataRequest<'_>) -> bool>
fn filterable( self, filter_name: &'static str, ) -> RequestFilterDataProvider<T, fn(DataRequest<'_>) -> bool>
Creates a filterable data provider with the given name for debugging. Read more
Source§impl<T> FromAngle<T> for T
impl<T> FromAngle<T> for T
Source§fn from_angle(angle: T) -> T
fn from_angle(angle: T) -> T
Performs a conversion from
angle.Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T, U> FromStimulus<U> for Twhere
U: IntoStimulus<T>,
impl<T, U> FromStimulus<U> for Twhere
U: IntoStimulus<T>,
Source§fn from_stimulus(other: U) -> T
fn from_stimulus(other: U) -> T
Converts
other into Self, while performing the appropriate scaling,
rounding and clamping.Source§impl<T, U> IntoAngle<U> for Twhere
U: FromAngle<T>,
impl<T, U> IntoAngle<U> for Twhere
U: FromAngle<T>,
Source§fn into_angle(self) -> U
fn into_angle(self) -> U
Performs a conversion into
T.Source§impl<WpParam, T, U> IntoCam16Unclamped<WpParam, T> for Uwhere
T: Cam16FromUnclamped<WpParam, U>,
impl<WpParam, T, U> IntoCam16Unclamped<WpParam, T> for Uwhere
T: Cam16FromUnclamped<WpParam, U>,
Source§type Scalar = <T as Cam16FromUnclamped<WpParam, U>>::Scalar
type Scalar = <T as Cam16FromUnclamped<WpParam, U>>::Scalar
The number type that’s used in
parameters when converting.Source§fn into_cam16_unclamped(
self,
parameters: BakedParameters<WpParam, <U as IntoCam16Unclamped<WpParam, T>>::Scalar>,
) -> T
fn into_cam16_unclamped( self, parameters: BakedParameters<WpParam, <U as IntoCam16Unclamped<WpParam, T>>::Scalar>, ) -> T
Converts
self into C, using the provided parameters.Source§impl<T, U> IntoColor<U> for Twhere
U: FromColor<T>,
impl<T, U> IntoColor<U> for Twhere
U: FromColor<T>,
Source§fn into_color(self) -> U
fn into_color(self) -> U
Convert into T with values clamped to the color defined bounds Read more
Source§impl<T, U> IntoColorUnclamped<U> for Twhere
U: FromColorUnclamped<T>,
impl<T, U> IntoColorUnclamped<U> for Twhere
U: FromColorUnclamped<T>,
Source§fn into_color_unclamped(self) -> U
fn into_color_unclamped(self) -> U
Convert into T. The resulting color might be invalid in its color space Read more
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
fn into_sample(self) -> T
Source§impl<T> IntoStimulus<T> for T
impl<T> IntoStimulus<T> for T
Source§fn into_stimulus(self) -> T
fn into_stimulus(self) -> T
Converts
self into T, while performing the appropriate scaling,
rounding and clamping.Source§impl<T> OverflowingAs for T
impl<T> OverflowingAs for T
Source§fn overflowing_as<Dst>(self) -> (Dst, bool)where
T: OverflowingCast<Dst>,
fn overflowing_as<Dst>(self) -> (Dst, bool)where
T: OverflowingCast<Dst>,
Casts the value.
Source§impl<Src, Dst> OverflowingCastFrom<Src> for Dstwhere
Src: OverflowingCast<Dst>,
impl<Src, Dst> OverflowingCastFrom<Src> for Dstwhere
Src: OverflowingCast<Dst>,
Source§fn overflowing_cast_from(src: Src) -> (Dst, bool)
fn overflowing_cast_from(src: Src) -> (Dst, bool)
Casts the value.
Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<R, P> ReadPrimitive<R> for P
impl<R, P> ReadPrimitive<R> for P
Source§fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
Read this value from the supplied reader. Same as
ReadEndian::read_from_little_endian().Source§impl<T> SaturatingAs for T
impl<T> SaturatingAs for T
Source§fn saturating_as<Dst>(self) -> Dstwhere
T: SaturatingCast<Dst>,
fn saturating_as<Dst>(self) -> Dstwhere
T: SaturatingCast<Dst>,
Casts the value.
Source§impl<Src, Dst> SaturatingCastFrom<Src> for Dstwhere
Src: SaturatingCast<Dst>,
impl<Src, Dst> SaturatingCastFrom<Src> for Dstwhere
Src: SaturatingCast<Dst>,
Source§fn saturating_cast_from(src: Src) -> Dst
fn saturating_cast_from(src: Src) -> Dst
Casts the value.
Source§impl<T> StrictAs for T
impl<T> StrictAs for T
Source§fn strict_as<Dst>(self) -> Dstwhere
T: StrictCast<Dst>,
fn strict_as<Dst>(self) -> Dstwhere
T: StrictCast<Dst>,
Casts the value.
Source§impl<Src, Dst> StrictCastFrom<Src> for Dstwhere
Src: StrictCast<Dst>,
impl<Src, Dst> StrictCastFrom<Src> for Dstwhere
Src: StrictCast<Dst>,
Source§fn strict_cast_from(src: Src) -> Dst
fn strict_cast_from(src: Src) -> Dst
Casts the value.
Source§impl<T, U> ToSample<U> for Twhere
U: FromSample<T>,
impl<T, U> ToSample<U> for Twhere
U: FromSample<T>,
fn to_sample_(self) -> U
Source§impl<T, C> TryComponentsInto<C> for Twhere
C: TryFromComponents<T>,
impl<T, C> TryComponentsInto<C> for Twhere
C: TryFromComponents<T>,
Source§type Error = <C as TryFromComponents<T>>::Error
type Error = <C as TryFromComponents<T>>::Error
The error for when
try_into_colors fails to cast.Source§fn try_components_into(self) -> Result<C, <T as TryComponentsInto<C>>::Error>
fn try_components_into(self) -> Result<C, <T as TryComponentsInto<C>>::Error>
Try to cast this collection of color components into a collection of
colors. Read more
Source§impl<T, U> TryIntoColor<U> for Twhere
U: TryFromColor<T>,
impl<T, U> TryIntoColor<U> for Twhere
U: TryFromColor<T>,
Source§fn try_into_color(self) -> Result<U, OutOfBounds<U>>
fn try_into_color(self) -> Result<U, OutOfBounds<U>>
Convert into T, returning ok if the color is inside of its defined
range, otherwise an
OutOfBounds error is returned which contains
the unclamped color. Read moreSource§impl<C, U> UintsFrom<C> for Uwhere
C: IntoUints<U>,
impl<C, U> UintsFrom<C> for Uwhere
C: IntoUints<U>,
Source§fn uints_from(colors: C) -> U
fn uints_from(colors: C) -> U
Cast a collection of colors into a collection of unsigned integers.
Source§impl<C, U> UintsInto<C> for Uwhere
C: FromUints<U>,
impl<C, U> UintsInto<C> for Uwhere
C: FromUints<U>,
Source§fn uints_into(self) -> C
fn uints_into(self) -> C
Cast this collection of unsigned integers into a collection of colors.
Source§impl<T> UnwrappedAs for T
impl<T> UnwrappedAs for T
Source§fn unwrapped_as<Dst>(self) -> Dstwhere
T: UnwrappedCast<Dst>,
fn unwrapped_as<Dst>(self) -> Dstwhere
T: UnwrappedCast<Dst>,
Casts the value.
Source§impl<Src, Dst> UnwrappedCastFrom<Src> for Dstwhere
Src: UnwrappedCast<Dst>,
impl<Src, Dst> UnwrappedCastFrom<Src> for Dstwhere
Src: UnwrappedCast<Dst>,
Source§fn unwrapped_cast_from(src: Src) -> Dst
fn unwrapped_cast_from(src: Src) -> Dst
Casts the value.
Source§impl<T> WrappingAs for T
impl<T> WrappingAs for T
Source§fn wrapping_as<Dst>(self) -> Dstwhere
T: WrappingCast<Dst>,
fn wrapping_as<Dst>(self) -> Dstwhere
T: WrappingCast<Dst>,
Casts the value.
Source§impl<Src, Dst> WrappingCastFrom<Src> for Dstwhere
Src: WrappingCast<Dst>,
impl<Src, Dst> WrappingCastFrom<Src> for Dstwhere
Src: WrappingCast<Dst>,
Source§fn wrapping_cast_from(src: Src) -> Dst
fn wrapping_cast_from(src: Src) -> Dst
Casts the value.