1use motion_canvas_rs::prelude::*;
2use std::time::Duration;
3
4const SLIDE: Duration = Duration::from_millis(2500);
5const BEAT: Duration = Duration::from_millis(800);
6
7fn make_source(w: f32, h: f32, font: f32) -> GroupNode {
9 GroupNode::new(vec![
10 Box::new(
11 Rect::default()
12 .with_size(Vec2::new(w, h))
13 .with_radius(w * 0.04)
14 .with_fill(linear_gradient!(
15 Color::rgb8(0xf4, 0x3f, 0x5e),
16 Color::rgb8(0x8b, 0x5c, 0xf6),
17 Color::rgb8(0x06, 0xb6, 0xd4)
18 )),
19 ),
20 Box::new(
21 Circle::default()
22 .with_position(Vec2::new(-w * 0.3, 0.0))
23 .with_radius(w * 0.12)
24 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 0x88), 3.0),
25 ),
26 Box::new(
27 Circle::default()
28 .with_position(Vec2::new(w * 0.3, 0.0))
29 .with_radius(w * 0.12)
30 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 0x88), 3.0),
31 ),
32 Box::new(
33 TextNode::default()
34 .with_text("MASKED")
35 .with_font_size(font)
36 .with_fill(Color::WHITE),
37 ),
38 ])
39}
40
41fn make_panel(
43 project: &mut Project,
44 x: f32,
45 y: f32,
46 mode: MaskMode,
47 name: &str,
48 color: Color,
49) -> (Circle, MaskNode, TextNode) {
50 let spot = Circle::default()
51 .with_position(Vec2::new(-110.0, 0.0))
52 .with_radius(50.0)
53 .with_fill(Color::WHITE);
54 let mask = MaskNode::new(
55 Box::new(spot.clone()),
56 Box::new(make_source(220.0, 130.0, 18.0)),
57 )
58 .with_position(Vec2::new(x, y))
59 .with_mode(mode)
60 .with_opacity(0.0);
61 let lbl = TextNode::default()
62 .with_position(Vec2::new(x, y + 90.0))
63 .with_text(name)
64 .with_font_size(15.0)
65 .with_fill(color)
66 .with_opacity(0.0);
67 project.scene.add(&mask);
68 project.scene.add(&lbl);
69 (spot, mask, lbl)
70}
71
72fn main() {
73 let mut project = Project::default()
74 .with_dimensions(1280, 720)
75 .with_fps(60)
76 .with_title("Mask Demo")
77 .with_background(Color::rgb8(0x0b, 0x0f, 0x19))
78 .close_on_finish();
79
80 for x in 1..26 {
82 for y in 1..15 {
83 project.scene.add(
84 Circle::default()
85 .with_position(Vec2::new(x as f32 * 50.0, y as f32 * 50.0))
86 .with_radius(1.0)
87 .with_fill(Color::rgb8(0x22, 0x2e, 0x4a)),
88 );
89 }
90 }
91
92 project.scene.add(
94 TextNode::default()
95 .with_position(Vec2::new(640.0, 50.0))
96 .with_text("Masking")
97 .with_font_size(36.0)
98 .with_fill(Color::rgb8(0xf8, 0xfa, 0xfc)),
99 );
100
101 let label = TextNode::default()
103 .with_position(Vec2::new(640.0, 100.0))
104 .with_text("Intersect (Source In)")
105 .with_font_size(20.0)
106 .with_fill(Color::rgb8(0x38, 0xbd, 0xf8));
107 project.scene.add(&label);
108
109 let spot = Circle::default()
111 .with_position(Vec2::new(-250.0, 0.0))
112 .with_radius(120.0)
113 .with_fill(Color::WHITE);
114
115 let big = MaskNode::new(
116 Box::new(spot.clone()),
117 Box::new(make_source(500.0, 300.0, 44.0)),
118 )
119 .with_position(Vec2::new(640.0, 380.0))
120 .with_mode(MaskMode::Intersect);
121 project.scene.add(&big);
122
123 let xs: [f32; 4] = [250.0, 510.0, 770.0, 1030.0];
125 let sy = 380.0;
126
127 let (s0, p0, t0) = make_panel(
128 &mut project,
129 xs[0],
130 sy,
131 MaskMode::Intersect,
132 "Intersect",
133 Color::rgb8(0x38, 0xbd, 0xf8),
134 );
135 let (s1, p1, t1) = make_panel(
136 &mut project,
137 xs[1],
138 sy,
139 MaskMode::Subtract,
140 "Subtract",
141 Color::rgb8(0xf4, 0x3f, 0x5e),
142 );
143 let (s2, p2, t2) = make_panel(
144 &mut project,
145 xs[2],
146 sy,
147 MaskMode::Exclude,
148 "Exclude",
149 Color::rgb8(0xa8, 0x55, 0xf7),
150 );
151 let (s3, p3, t3) = make_panel(
152 &mut project,
153 xs[3],
154 sy,
155 MaskMode::Union,
156 "Union",
157 Color::rgb8(0x10, 0xb9, 0x81),
158 );
159
160 project.scene.video_timeline.add(loop_anim!(
162 chain![
163 wait(BEAT),
164 spot.position.to(Vec2::new(250.0, 0.0), SLIDE),
166 wait(BEAT),
167 all![
169 spot.position.to(Vec2::new(-250.0, 0.0), Duration::ZERO),
170 label
171 .text
172 .to("Subtract (Source Out)".into(), Duration::ZERO),
173 label
174 .fill_paint
175 .to(Paint::Solid(Color::rgb8(0xf4, 0x3f, 0x5e)), Duration::ZERO),
176 big.mode.to(MaskMode::Subtract, Duration::ZERO),
177 ],
178 wait(BEAT),
179 spot.position.to(Vec2::new(250.0, 0.0), SLIDE),
181 wait(BEAT),
182 all![
184 spot.position.to(Vec2::new(-250.0, 0.0), Duration::ZERO),
185 label.text.to("Exclude (XOR)".into(), Duration::ZERO),
186 label
187 .fill_paint
188 .to(Paint::Solid(Color::rgb8(0xa8, 0x55, 0xf7)), Duration::ZERO),
189 big.mode.to(MaskMode::Exclude, Duration::ZERO),
190 ],
191 wait(BEAT),
192 spot.position.to(Vec2::new(250.0, 0.0), SLIDE),
194 wait(BEAT),
195 all![
197 spot.position.to(Vec2::new(-250.0, 0.0), Duration::ZERO),
198 label.text.to("Union (Source Over)".into(), Duration::ZERO),
199 label
200 .fill_paint
201 .to(Paint::Solid(Color::rgb8(0x10, 0xb9, 0x81)), Duration::ZERO),
202 big.mode.to(MaskMode::Union, Duration::ZERO),
203 ],
204 wait(BEAT),
205 spot.position.to(Vec2::new(250.0, 0.0), SLIDE),
207 wait(BEAT),
208 all![
210 big.opacity.to(0.0, Duration::ZERO),
211 label.text.to("All Modes".into(), Duration::ZERO),
212 label
213 .fill_paint
214 .to(Paint::Solid(Color::rgb8(0xe2, 0xe8, 0xf0)), Duration::ZERO),
215 p0.opacity.to(1.0, Duration::ZERO),
216 p1.opacity.to(1.0, Duration::ZERO),
217 p2.opacity.to(1.0, Duration::ZERO),
218 p3.opacity.to(1.0, Duration::ZERO),
219 t0.opacity.to(1.0, Duration::ZERO),
220 t1.opacity.to(1.0, Duration::ZERO),
221 t2.opacity.to(1.0, Duration::ZERO),
222 t3.opacity.to(1.0, Duration::ZERO),
223 ],
224 wait(BEAT),
225 all![
227 s0.position.to(Vec2::new(110.0, 0.0), SLIDE),
228 s1.position.to(Vec2::new(110.0, 0.0), SLIDE),
229 s2.position.to(Vec2::new(110.0, 0.0), SLIDE),
230 s3.position.to(Vec2::new(110.0, 0.0), SLIDE),
231 ],
232 wait(BEAT),
233 all![
235 p0.opacity.to(0.0, Duration::ZERO),
237 p1.opacity.to(0.0, Duration::ZERO),
238 p2.opacity.to(0.0, Duration::ZERO),
239 p3.opacity.to(0.0, Duration::ZERO),
240 t0.opacity.to(0.0, Duration::ZERO),
241 t1.opacity.to(0.0, Duration::ZERO),
242 t2.opacity.to(0.0, Duration::ZERO),
243 t3.opacity.to(0.0, Duration::ZERO),
244 s0.position.to(Vec2::new(-110.0, 0.0), Duration::ZERO),
246 s1.position.to(Vec2::new(-110.0, 0.0), Duration::ZERO),
247 s2.position.to(Vec2::new(-110.0, 0.0), Duration::ZERO),
248 s3.position.to(Vec2::new(-110.0, 0.0), Duration::ZERO),
249 big.opacity.to(1.0, Duration::ZERO),
251 big.mode.to(MaskMode::Intersect, Duration::ZERO),
252 spot.position.to(Vec2::new(-250.0, 0.0), Duration::ZERO),
253 label
254 .text
255 .to("Intersect (Source In)".into(), Duration::ZERO),
256 label
257 .fill_paint
258 .to(Paint::Solid(Color::rgb8(0x38, 0xbd, 0xf8)), Duration::ZERO),
259 ],
260 ],
261 None
262 ));
263
264 project.show().expect("Failed to render");
265}