pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}Expand description
32-bit RGBA color.
Fields§
§r: u8Red component.
g: u8Green component.
b: u8Blue component.
a: u8Alpha component.
Implementations§
Source§impl Color
impl Color
Sourcepub const fn rgb8(r: u8, g: u8, b: u8) -> Color
pub const fn rgb8(r: u8, g: u8, b: u8) -> Color
Creates a new RGB color with 255 alpha.
Examples found in repository?
6const BG: Color = Color::rgb8(0x0e, 0x0e, 0x12);
7const WHITE: Color = Color::rgb8(0xf0, 0xf0, 0xf0);
8const DIM: Color = Color::rgb8(0x55, 0x55, 0x66);
9const ACCENT: Color = Color::rgb8(0x68, 0xab, 0xdf);
10const RED: Color = Color::rgb8(0xe1, 0x32, 0x38);
11const YELLOW: Color = Color::rgb8(0xe6, 0xa7, 0x00);
12const GREEN: Color = Color::rgb8(0x25, 0xc2, 0x81);
13const TEAL: Color = Color::rgb8(0x20, 0xb2, 0xaa);
14
15const CANVAS_W: u32 = 1280;
16const CANVAS_H: u32 = 720;
17const LEFT: f32 = 40.0;
18
19fn ms(n: u64) -> Duration {
20 Duration::from_millis(n)
21}
22fn secs(n: u64) -> Duration {
23 Duration::from_secs(n)
24}
25
26// ── Text Helpers ───────────────────────────────────────────────────────────
27fn title(text: &str, y: f32) -> TextNode {
28 TextNode::default()
29 .with_anchor(Vec2::new(-1.0, -1.0))
30 .with_position(Vec2::new(LEFT, y))
31 .with_text(text)
32 .with_font_size(36.0)
33 .with_fill(ACCENT)
34 .with_font(FONT)
35 .with_opacity(0.0)
36}
37fn h2(text: &str, y: f32) -> TextNode {
38 TextNode::default()
39 .with_anchor(Vec2::new(-1.0, -1.0))
40 .with_position(Vec2::new(LEFT, y))
41 .with_text(text)
42 .with_font_size(22.0)
43 .with_fill(WHITE)
44 .with_font(FONT)
45 .with_opacity(0.0)
46}
47fn body(text: &str, y: f32) -> TextNode {
48 TextNode::default()
49 .with_anchor(Vec2::new(-1.0, -1.0))
50 .with_position(Vec2::new(LEFT, y))
51 .with_text(text)
52 .with_font_size(17.0)
53 .with_fill(Color::rgb8(0xcc, 0xcc, 0xdd))
54 .with_font(FONT)
55 .with_opacity(0.0)
56}More examples
7const BG: Color = Color::rgb8(0x0e, 0x0e, 0x12);
8const WHITE: Color = Color::rgb8(0xf0, 0xf0, 0xf0);
9const DIM: Color = Color::rgb8(0x55, 0x55, 0x66);
10const ACCENT: Color = Color::rgb8(0x68, 0xab, 0xdf);
11const YELLOW: Color = Color::rgb8(0xe6, 0xa7, 0x00);
12const GREEN: Color = Color::rgb8(0x25, 0xc2, 0x81);
13const RED: Color = Color::rgb8(0xe1, 0x32, 0x38);
14
15fn show(opacity: &Signal<f32>, d: Duration) -> Box<dyn Animation> {
16 opacity.to(1.0, d).ease(easings::cubic_out).into()
17}
18
19fn hide(opacity: &Signal<f32>, d: Duration) -> Box<dyn Animation> {
20 opacity.to(0.0, d).ease(easings::cubic_in).into()
21}
22
23// Helper for boxes to ensure they start completely hidden and slightly scaled down
24fn create_box(label: &str, center: Vec2, size: Vec2) -> (Rect, TextNode) {
25 let box_center = Vec2::new(center.x, center.y);
26
27 let text_pos = Vec2::new(center.x, center.y);
28
29 let r = Rect::default()
30 .with_position(box_center)
31 .with_size(size)
32 .with_scale(0.95)
33 .with_opacity(0.0)
34 .with_radius(8.0)
35 .with_stroke(ACCENT, 2.0)
36 .with_fill(Color::rgb8(0x26, 0x26, 0x2a));
37
38 let t = TextNode::default()
39 .with_position(text_pos)
40 .with_text(label)
41 .with_font_size(18.0)
42 .with_font("JetBrains Mono")
43 .with_fill(WHITE)
44 .with_scale(0.95)
45 .with_opacity(0.0);
46
47 (r, t)
48}80const CYAN: Color = Color::rgb8(0x00, 0xf2, 0xfe);
81const INDIGO: Color = Color::rgb8(0x4f, 0x46, 0xe5);
82const CORAL: Color = Color::rgb8(0xff, 0x5e, 0x3a);
83const PINK: Color = Color::rgb8(0xff, 0x2a, 0x68);
84const VIOLET: Color = Color::rgb8(0x92, 0x3c, 0xff);
85const FUCHSIA: Color = Color::rgb8(0xff, 0x00, 0x7f);
86const BG: Color = Color::rgb8(0x0a, 0x0a, 0x12);
87const GRID_COLOR: Color = Color::rgba8(0x20, 0x20, 0x38, 60);
88const LABEL: Color = Color::rgb8(0x99, 0x99, 0xbb);
89
90// Timing
91const FADE_IN: Duration = Duration::from_millis(500);
92const FADE_OUT: Duration = Duration::from_millis(300);
93const MORPH: Duration = Duration::from_millis(1800);
94
95fn main() {
96 let cx = 960.0_f32;
97 let cy = 480.0_f32;
98
99 let mut project = Project::default()
100 .with_dimensions(1920, 1080)
101 .with_cache(true)
102 .with_title("Gradient Demo")
103 .close_on_finish();
104
105 // Background
106 let bg_rect = Rect::default()
107 .with_position(Vec2::new(cx, 540.0))
108 .with_size(Vec2::new(1920.0, 1080.0))
109 .with_fill(BG);
110
111 let grid = GridNode::default()
112 .with_position(Vec2::new(cx, 540.0))
113 .with_columns(32.0)
114 .with_rows(18.0)
115 .with_spacing_all(60.0)
116 .with_stroke(GRID_COLOR, 1.0)
117 .with_opacity(0.0);
118
119 // Gradients
120 let sunset = tri_grad(CORAL, PINK, VIOLET, 120.0);
121 let ocean = linear_grad(CYAN, INDIGO, 120.0);
122 let glow = radial_grad(FUCHSIA, Color::rgba8(0x4f, 0x46, 0xe5, 0), 130.0);
123 let glow_alt = radial_grad(CYAN, Color::rgba8(0x00, 0xf2, 0xfe, 0), 180.0);
124
125 // Sunset in different directions (same colors, different orientation)
126 let sunset_stops = vec![
127 ColorStop {
128 offset: 0.0,
129 color: CORAL,
130 },
131 ColorStop {
132 offset: 0.5,
133 color: PINK,
134 },
135 ColorStop {
136 offset: 1.0,
137 color: VIOLET,
138 },
139 ];
140 let sunset_horiz = directed_grad(
141 Point::new(-120.0, 0.0),
142 Point::new(120.0, 0.0),
143 sunset_stops.clone(),
144 );
145 let sunset_vert = directed_grad(
146 Point::new(0.0, -120.0),
147 Point::new(0.0, 120.0),
148 sunset_stops.clone(),
149 );
150 let sunset_diag = directed_grad(
151 Point::new(-120.0, -120.0),
152 Point::new(120.0, 120.0),
153 sunset_stops.clone(),
154 );
155
156 // Ocean in different directions
157 let ocean_stops = vec![
158 ColorStop {
159 offset: 0.0,
160 color: CYAN,
161 },
162 ColorStop {
163 offset: 1.0,
164 color: INDIGO,
165 },
166 ];
167 let ocean_vert = directed_grad(
168 Point::new(0.0, -120.0),
169 Point::new(0.0, 120.0),
170 ocean_stops.clone(),
171 );
172
173 // Persistent title
174 let title = TextNode::default()
175 .with_position(Vec2::new(cx, 100.0))
176 .with_text("Gradient Paint Demo")
177 .with_font_size(48.0)
178 .with_fill(Color::rgb8(0xdd, 0xdd, 0xee))
179 .with_opacity(0.0);
180
181 // Per-scene label (reused position, swapped text via separate nodes)
182 let label_y = 200.0;
183
184 // ─── SCENE 1: Circle with linear gradient fill ───
185 let s1_lbl = TextNode::default()
186 .with_position(Vec2::new(cx, label_y))
187 .with_text("Linear Gradient Fill")
188 .with_font_size(28.0)
189 .with_fill(LABEL)
190 .with_opacity(0.0);
191
192 let s1 = Circle::default()
193 .with_position(Vec2::new(cx, cy))
194 .with_radius(130.0)
195 .with_fill(sunset.clone())
196 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 20), 2.0)
197 .with_scale(0.95)
198 .with_opacity(0.0);
199
200 // Guides
201 let s1_g_start = Circle::default()
202 .with_position(Vec2::new(cx - 120.0, cy - 120.0))
203 .with_radius(8.0)
204 .with_fill(Color::WHITE)
205 .with_stroke(CORAL, 2.5)
206 .with_opacity(0.0);
207
208 let s1_g_end = Circle::default()
209 .with_position(Vec2::new(cx + 120.0, cy + 120.0))
210 .with_radius(8.0)
211 .with_fill(Color::WHITE)
212 .with_stroke(VIOLET, 2.5)
213 .with_opacity(0.0);
214
215 let s1_g_line = Line::default()
216 .with_position(Vec2::new(cx, cy))
217 .with_start(Vec2::new(-120.0, -120.0))
218 .with_end(Vec2::new(120.0, 120.0))
219 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
220 .with_opacity(0.0);
221
222 // ─── SCENE 2: Rect with radial gradient fill ───
223 let s2_lbl = TextNode::default()
224 .with_position(Vec2::new(cx, label_y))
225 .with_text("Radial Gradient Fill")
226 .with_font_size(28.0)
227 .with_fill(LABEL)
228 .with_opacity(0.0);
229
230 let s2 = Rect::default()
231 .with_position(Vec2::new(cx, cy))
232 .with_size(Vec2::new(260.0, 260.0))
233 .with_radius(24.0)
234 .with_fill(glow.clone())
235 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 20), 2.0)
236 .with_scale(0.95)
237 .with_opacity(0.0);
238
239 // Guides
240 let s2_g_center = Circle::default()
241 .with_position(Vec2::new(cx, cy))
242 .with_radius(8.0)
243 .with_fill(Color::WHITE)
244 .with_stroke(FUCHSIA, 2.5)
245 .with_opacity(0.0);
246
247 let s2_g_radius = Circle::default()
248 .with_position(Vec2::new(cx, cy))
249 .with_radius(130.0)
250 .with_fill(Color::TRANSPARENT)
251 .with_stroke(Color::rgba8(0xff, 0x00, 0x7f, 80), 1.5)
252 .with_opacity(0.0);
253
254 // ─── SCENE 3: Hexagon with gradient stroke ───
255 let s3_lbl = TextNode::default()
256 .with_position(Vec2::new(cx, label_y))
257 .with_text("Gradient Stroke")
258 .with_font_size(28.0)
259 .with_fill(LABEL)
260 .with_opacity(0.0);
261
262 let s3 = Polygon::regular(6, 130.0)
263 .with_position(Vec2::new(cx, cy))
264 .with_fill(Color::rgba8(0x12, 0x12, 0x20, 255))
265 .with_stroke(ocean.clone(), 5.0)
266 .with_scale(0.95)
267 .with_opacity(0.0);
268
269 // Guides
270 let s3_g_start = Circle::default()
271 .with_position(Vec2::new(cx - 120.0, cy))
272 .with_radius(8.0)
273 .with_fill(Color::WHITE)
274 .with_stroke(CYAN, 2.5)
275 .with_opacity(0.0);
276
277 let s3_g_end = Circle::default()
278 .with_position(Vec2::new(cx + 120.0, cy))
279 .with_radius(8.0)
280 .with_fill(Color::WHITE)
281 .with_stroke(INDIGO, 2.5)
282 .with_opacity(0.0);
283
284 let s3_g_line = Line::default()
285 .with_position(Vec2::new(cx, cy))
286 .with_start(Vec2::new(-120.0, 0.0))
287 .with_end(Vec2::new(120.0, 0.0))
288 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
289 .with_opacity(0.0);
290
291 // ─── SCENE 4: Line with gradient stroke ───
292 let s4_lbl = TextNode::default()
293 .with_position(Vec2::new(cx, label_y))
294 .with_text("Gradient Line")
295 .with_font_size(28.0)
296 .with_fill(LABEL)
297 .with_opacity(0.0);
298
299 let s4 = Line::default()
300 .with_position(Vec2::new(cx, cy))
301 .with_start(Vec2::new(-160.0, 100.0))
302 .with_end(Vec2::new(160.0, -100.0))
303 .with_stroke(sunset.clone(), 8.0)
304 .with_opacity(0.0);
305
306 // Guides
307 let s4_g_start = Circle::default()
308 .with_position(Vec2::new(cx - 120.0, cy - 120.0))
309 .with_radius(8.0)
310 .with_fill(Color::WHITE)
311 .with_stroke(CORAL, 2.5)
312 .with_opacity(0.0);
313
314 let s4_g_end = Circle::default()
315 .with_position(Vec2::new(cx + 120.0, cy + 120.0))
316 .with_radius(8.0)
317 .with_fill(Color::WHITE)
318 .with_stroke(VIOLET, 2.5)
319 .with_opacity(0.0);
320
321 let s4_g_line = Line::default()
322 .with_position(Vec2::new(cx, cy))
323 .with_start(Vec2::new(-120.0, -120.0))
324 .with_end(Vec2::new(120.0, 120.0))
325 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
326 .with_opacity(0.0);
327
328 // ─── SCENE 5: Path with gradient stroke ───
329 let s5_lbl = TextNode::default()
330 .with_position(Vec2::new(cx, label_y))
331 .with_text("Gradient Path")
332 .with_font_size(28.0)
333 .with_fill(LABEL)
334 .with_opacity(0.0);
335
336 let mut tri_path = BezPath::new();
337 tri_path.move_to(kurbo::Point::new(0.0, -120.0));
338 tri_path.line_to(kurbo::Point::new(104.0, 60.0));
339 tri_path.line_to(kurbo::Point::new(-104.0, 60.0));
340 tri_path.close_path();
341
342 let s5 = PathNode::new(Vec2::new(cx, cy), tri_path, CYAN, 6.0)
343 .with_stroke(ocean.clone(), 6.0)
344 .with_opacity(0.0);
345
346 // Guides
347 let s5_g_start = Circle::default()
348 .with_position(Vec2::new(cx - 120.0, cy))
349 .with_radius(8.0)
350 .with_fill(Color::WHITE)
351 .with_stroke(CYAN, 2.5)
352 .with_opacity(0.0);
353
354 let s5_g_end = Circle::default()
355 .with_position(Vec2::new(cx + 120.0, cy))
356 .with_radius(8.0)
357 .with_fill(Color::WHITE)
358 .with_stroke(INDIGO, 2.5)
359 .with_opacity(0.0);
360
361 let s5_g_line = Line::default()
362 .with_position(Vec2::new(cx, cy))
363 .with_start(Vec2::new(-120.0, 0.0))
364 .with_end(Vec2::new(120.0, 0.0))
365 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
366 .with_opacity(0.0);
367
368 // ─── SCENE 6: Grid with gradient stroke ───
369 let s6_lbl = TextNode::default()
370 .with_position(Vec2::new(cx, label_y))
371 .with_text("Gradient Grid")
372 .with_font_size(28.0)
373 .with_fill(LABEL)
374 .with_opacity(0.0);
375
376 let s6_grid = GridNode::default()
377 .with_position(Vec2::new(cx, cy))
378 .with_columns(8.0)
379 .with_rows(6.0)
380 .with_spacing_all(50.0)
381 .with_stroke(ocean.clone(), 2.0)
382 .with_opacity(0.0);
383
384 // Guides
385 let s6_g_start = Circle::default()
386 .with_position(Vec2::new(cx - 120.0, cy))
387 .with_radius(8.0)
388 .with_fill(Color::WHITE)
389 .with_stroke(CYAN, 2.5)
390 .with_opacity(0.0);
391
392 let s6_g_end = Circle::default()
393 .with_position(Vec2::new(cx + 120.0, cy))
394 .with_radius(8.0)
395 .with_fill(Color::WHITE)
396 .with_stroke(INDIGO, 2.5)
397 .with_opacity(0.0);
398
399 let s6_g_line = Line::default()
400 .with_position(Vec2::new(cx, cy))
401 .with_start(Vec2::new(-120.0, 0.0))
402 .with_end(Vec2::new(120.0, 0.0))
403 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
404 .with_opacity(0.0);
405
406 // ─── SCENE 7: Text with gradient fill ───
407 let s7_lbl = TextNode::default()
408 .with_position(Vec2::new(cx, label_y))
409 .with_text("Gradient Text")
410 .with_font_size(28.0)
411 .with_fill(LABEL)
412 .with_opacity(0.0);
413
414 let s7 = TextNode::default()
415 .with_position(Vec2::new(cx, cy))
416 .with_text("HELLO GRADIENTS")
417 .with_font_size(80.0)
418 .with_fill(ocean.clone())
419 .with_scale(0.95)
420 .with_opacity(0.0);
421
422 // Guides
423 let s7_g_start = Circle::default()
424 .with_position(Vec2::new(cx - 120.0, cy))
425 .with_radius(8.0)
426 .with_fill(Color::WHITE)
427 .with_stroke(CYAN, 2.5)
428 .with_opacity(0.0);
429
430 let s7_g_end = Circle::default()
431 .with_position(Vec2::new(cx + 120.0, cy))
432 .with_radius(8.0)
433 .with_fill(Color::WHITE)
434 .with_stroke(INDIGO, 2.5)
435 .with_opacity(0.0);
436
437 let s7_g_line = Line::default()
438 .with_position(Vec2::new(cx, cy))
439 .with_start(Vec2::new(-120.0, 0.0))
440 .with_end(Vec2::new(120.0, 0.0))
441 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
442 .with_opacity(0.0);
443
444 // ─── SCENE 8: Math with gradient fill ───
445 let s8_lbl = TextNode::default()
446 .with_position(Vec2::new(cx, label_y))
447 .with_text("Gradient Math")
448 .with_font_size(28.0)
449 .with_fill(LABEL)
450 .with_opacity(0.0);
451
452 let s8 = MathNode::default()
453 .with_position(Vec2::new(cx, cy))
454 .with_equation("E = m c^2")
455 .with_font_size(72.0)
456 .with_fill(sunset.clone())
457 .with_scale(0.95)
458 .with_opacity(0.0);
459
460 // Guides
461 let s8_g_start = Circle::default()
462 .with_position(Vec2::new(cx - 120.0, cy - 120.0))
463 .with_radius(8.0)
464 .with_fill(Color::WHITE)
465 .with_stroke(CORAL, 2.5)
466 .with_opacity(0.0);
467
468 let s8_g_end = Circle::default()
469 .with_position(Vec2::new(cx + 120.0, cy + 120.0))
470 .with_radius(8.0)
471 .with_fill(Color::WHITE)
472 .with_stroke(VIOLET, 2.5)
473 .with_opacity(0.0);
474
475 let s8_g_line = Line::default()
476 .with_position(Vec2::new(cx, cy))
477 .with_start(Vec2::new(-120.0, -120.0))
478 .with_end(Vec2::new(120.0, 120.0))
479 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
480 .with_opacity(0.0);
481
482 // ─── SCENE 9: Solid ↔ Gradient ───
483 let s9_lbl = TextNode::default()
484 .with_position(Vec2::new(cx, label_y))
485 .with_text("Solid to Gradient")
486 .with_font_size(28.0)
487 .with_fill(LABEL)
488 .with_opacity(0.0);
489
490 let s9 = Circle::default()
491 .with_position(Vec2::new(cx, cy))
492 .with_radius(130.0)
493 .with_fill(Color::WHITE)
494 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 20), 2.0)
495 .with_scale(0.95)
496 .with_opacity(0.0);
497
498 // Guides (start with opacity 0, fade in when Sunset is active, fade out on Solid)
499 let s9_g_start = Circle::default()
500 .with_position(Vec2::new(cx - 120.0, cy - 120.0))
501 .with_radius(8.0)
502 .with_fill(Color::WHITE)
503 .with_stroke(CORAL, 2.5)
504 .with_opacity(0.0);
505
506 let s9_g_end = Circle::default()
507 .with_position(Vec2::new(cx + 120.0, cy + 120.0))
508 .with_radius(8.0)
509 .with_fill(Color::WHITE)
510 .with_stroke(VIOLET, 2.5)
511 .with_opacity(0.0);
512
513 let s9_g_line = Line::default()
514 .with_position(Vec2::new(cx, cy))
515 .with_start(Vec2::new(-120.0, -120.0))
516 .with_end(Vec2::new(120.0, 120.0))
517 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
518 .with_opacity(0.0);
519
520 // ─── SCENE 10: Radial ↔ Linear Interpolation ───
521 let s10_lbl = TextNode::default()
522 .with_position(Vec2::new(cx, label_y))
523 .with_text("Radial ↔ Linear Morph")
524 .with_font_size(28.0)
525 .with_fill(LABEL)
526 .with_opacity(0.0);
527
528 let s10 = Circle::default()
529 .with_position(Vec2::new(cx, cy))
530 .with_radius(130.0)
531 .with_fill(glow.clone()) // Start as Radial Gradient
532 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 20), 2.0)
533 .with_scale(0.95)
534 .with_opacity(0.0);
535
536 // Visual guides for Scene 10 (cross-fading between radial and linear handles)
537 let s10_g_rad_center = Circle::default()
538 .with_position(Vec2::new(cx, cy))
539 .with_radius(8.0)
540 .with_fill(Color::WHITE)
541 .with_stroke(FUCHSIA, 2.5)
542 .with_opacity(0.0);
543
544 let s10_g_rad_radius = Circle::default()
545 .with_position(Vec2::new(cx, cy))
546 .with_radius(130.0)
547 .with_fill(Color::TRANSPARENT)
548 .with_stroke(Color::rgba8(0xff, 0x00, 0x7f, 80), 1.5)
549 .with_opacity(0.0);
550
551 let s10_g_lin_start = Circle::default()
552 .with_position(Vec2::new(cx - 120.0, cy - 120.0))
553 .with_radius(8.0)
554 .with_fill(Color::WHITE)
555 .with_stroke(CORAL, 2.5)
556 .with_opacity(0.0);
557
558 let s10_g_lin_end = Circle::default()
559 .with_position(Vec2::new(cx + 120.0, cy + 120.0))
560 .with_radius(8.0)
561 .with_fill(Color::WHITE)
562 .with_stroke(VIOLET, 2.5)
563 .with_opacity(0.0);
564
565 let s10_g_lin_line = Line::default()
566 .with_position(Vec2::new(cx, cy))
567 .with_start(Vec2::new(-120.0, -120.0))
568 .with_end(Vec2::new(120.0, 120.0))
569 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
570 .with_opacity(0.0);
571
572 // Add all to scene
573 project.scene.add(&bg_rect);
574 project.scene.add(&grid);
575 project.scene.add(&title);
576
577 // Scene 1
578 project.scene.add(&s1_lbl);
579 project.scene.add(&s1);
580 project.scene.add(&s1_g_line);
581 project.scene.add(&s1_g_start);
582 project.scene.add(&s1_g_end);
583
584 // Scene 2
585 project.scene.add(&s2_lbl);
586 project.scene.add(&s2);
587 project.scene.add(&s2_g_radius);
588 project.scene.add(&s2_g_center);
589
590 // Scene 3
591 project.scene.add(&s3_lbl);
592 project.scene.add(&s3);
593 project.scene.add(&s3_g_line);
594 project.scene.add(&s3_g_start);
595 project.scene.add(&s3_g_end);
596
597 // Scene 4
598 project.scene.add(&s4_lbl);
599 project.scene.add(&s4);
600 project.scene.add(&s4_g_line);
601 project.scene.add(&s4_g_start);
602 project.scene.add(&s4_g_end);
603
604 // Scene 5
605 project.scene.add(&s5_lbl);
606 project.scene.add(&s5);
607 project.scene.add(&s5_g_line);
608 project.scene.add(&s5_g_start);
609 project.scene.add(&s5_g_end);
610
611 // Scene 6
612 project.scene.add(&s6_lbl);
613 project.scene.add(&s6_grid);
614 project.scene.add(&s6_g_line);
615 project.scene.add(&s6_g_start);
616 project.scene.add(&s6_g_end);
617
618 // Scene 7
619 project.scene.add(&s7_lbl);
620 project.scene.add(&s7);
621 project.scene.add(&s7_g_line);
622 project.scene.add(&s7_g_start);
623 project.scene.add(&s7_g_end);
624
625 // Scene 8
626 project.scene.add(&s8_lbl);
627 project.scene.add(&s8);
628 project.scene.add(&s8_g_line);
629 project.scene.add(&s8_g_start);
630 project.scene.add(&s8_g_end);
631
632 // Scene 9
633 project.scene.add(&s9_lbl);
634 project.scene.add(&s9);
635 project.scene.add(&s9_g_line);
636 project.scene.add(&s9_g_start);
637 project.scene.add(&s9_g_end);
638
639 // Scene 10
640 project.scene.add(&s10_lbl);
641 project.scene.add(&s10);
642 project.scene.add(&s10_g_rad_radius);
643 project.scene.add(&s10_g_rad_center);
644 project.scene.add(&s10_g_lin_line);
645 project.scene.add(&s10_g_lin_start);
646 project.scene.add(&s10_g_lin_end);
647
648 // Timeline — one scene at a time, each with its own gradient animation
649 project.scene.video_timeline.add(chain![
650 // Intro
651 all![
652 grid.opacity.to(1.0, Duration::from_millis(800)),
653 title.opacity.to(1.0, Duration::from_millis(800)),
654 ],
655 wait!(1),
656 // ─── 1: Circle linear fill — morph colors, then direction ───
657 all![
658 s1_lbl.opacity.to(1.0, FADE_IN),
659 s1.opacity.to(1.0, FADE_IN),
660 s1.scale.to(Vec2::ONE, FADE_IN),
661 s1_g_start.opacity.to(1.0, FADE_IN),
662 s1_g_end.opacity.to(1.0, FADE_IN),
663 s1_g_line.opacity.to(1.0, FADE_IN),
664 ],
665 wait!(1),
666 // Color morph: sunset → ocean
667 all![
668 s1.fill_paint.to(Paint::Gradient(ocean.clone()), MORPH),
669 s1_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
670 s1_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
671 s1_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
672 s1_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
673 s1_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
674 s1_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
675 ],
676 wait!(1),
677 // Color morph: ocean → sunset
678 all![
679 s1.fill_paint.to(Paint::Gradient(sunset.clone()), MORPH),
680 s1_g_start
681 .position
682 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
683 s1_g_end
684 .position
685 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
686 s1_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
687 s1_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
688 s1_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
689 s1_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
690 ],
691 wait!(1),
692 // Direction morph: diagonal → horizontal sunset
693 all![
694 s1.fill_paint
695 .to(Paint::Gradient(sunset_horiz.clone()), MORPH),
696 s1_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
697 s1_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
698 s1_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
699 s1_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
700 ],
701 wait!(1),
702 // Direction morph: horizontal → vertical sunset
703 all![
704 s1.fill_paint
705 .to(Paint::Gradient(sunset_vert.clone()), MORPH),
706 s1_g_start.position.to(Vec2::new(cx, cy - 120.0), MORPH),
707 s1_g_end.position.to(Vec2::new(cx, cy + 120.0), MORPH),
708 s1_g_line.start.to(Vec2::new(0.0, -120.0), MORPH),
709 s1_g_line.end.to(Vec2::new(0.0, 120.0), MORPH),
710 ],
711 wait!(1),
712 // Direction morph: vertical → diagonal sunset
713 all![
714 s1.fill_paint
715 .to(Paint::Gradient(sunset_diag.clone()), MORPH),
716 s1_g_start
717 .position
718 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
719 s1_g_end
720 .position
721 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
722 s1_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
723 s1_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
724 ],
725 wait!(1),
726 all![
727 s1_lbl.opacity.to(0.0, FADE_OUT),
728 s1.opacity.to(0.0, FADE_OUT),
729 s1.scale.to(Vec2::splat(0.95), FADE_OUT),
730 s1_g_start.opacity.to(0.0, FADE_OUT),
731 s1_g_end.opacity.to(0.0, FADE_OUT),
732 s1_g_line.opacity.to(0.0, FADE_OUT),
733 ],
734 // ─── 2: Rect radial fill — morph to another radial gradient and back ───
735 all![
736 s2_lbl.opacity.to(1.0, FADE_IN),
737 s2.opacity.to(1.0, FADE_IN),
738 s2.scale.to(Vec2::ONE, FADE_IN),
739 s2_g_center.opacity.to(1.0, FADE_IN),
740 s2_g_radius.opacity.to(1.0, FADE_IN),
741 ],
742 wait!(1),
743 // Radial color and radius morph: glow (Fuchsia 130px) -> glow_alt (Cyan 180px)
744 all![
745 s2.fill_paint.to(Paint::Gradient(glow_alt.clone()), MORPH),
746 s2_g_radius.radius.to(180.0, MORPH),
747 s2_g_center.stroke_paint.to(Paint::Solid(CYAN), MORPH),
748 s2_g_radius
749 .stroke_paint
750 .to(Paint::Solid(Color::rgba8(0x00, 0xf2, 0xfe, 80)), MORPH),
751 ],
752 wait!(1),
753 // Morph back to glow
754 all![
755 s2.fill_paint.to(Paint::Gradient(glow.clone()), MORPH),
756 s2_g_radius.radius.to(130.0, MORPH),
757 s2_g_center.stroke_paint.to(Paint::Solid(FUCHSIA), MORPH),
758 s2_g_radius
759 .stroke_paint
760 .to(Paint::Solid(Color::rgba8(0xff, 0x00, 0x7f, 80)), MORPH),
761 ],
762 wait!(1),
763 all![
764 s2_lbl.opacity.to(0.0, FADE_OUT),
765 s2.opacity.to(0.0, FADE_OUT),
766 s2.scale.to(Vec2::splat(0.95), FADE_OUT),
767 s2_g_center.opacity.to(0.0, FADE_OUT),
768 s2_g_radius.opacity.to(0.0, FADE_OUT),
769 ],
770 // ─── 3: Hex gradient stroke — morph colors, then direction ───
771 all![
772 s3_lbl.opacity.to(1.0, FADE_IN),
773 s3.opacity.to(1.0, FADE_IN),
774 s3.scale.to(Vec2::ONE, FADE_IN),
775 s3_g_start.opacity.to(1.0, FADE_IN),
776 s3_g_end.opacity.to(1.0, FADE_IN),
777 s3_g_line.opacity.to(1.0, FADE_IN),
778 ],
779 wait!(1),
780 // Morph: ocean (horizontal) -> sunset (diagonal)
781 all![
782 s3.stroke_paint.to(Paint::Gradient(sunset.clone()), MORPH),
783 s3_g_start
784 .position
785 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
786 s3_g_end
787 .position
788 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
789 s3_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
790 s3_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
791 s3_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
792 s3_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
793 ],
794 wait!(1),
795 // Morph: sunset (diagonal) -> ocean (horizontal)
796 all![
797 s3.stroke_paint.to(Paint::Gradient(ocean.clone()), MORPH),
798 s3_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
799 s3_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
800 s3_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
801 s3_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
802 s3_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
803 s3_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
804 ],
805 wait!(1),
806 // Direction morph: horizontal ocean -> vertical ocean
807 all![
808 s3.stroke_paint
809 .to(Paint::Gradient(ocean_vert.clone()), MORPH),
810 s3_g_start.position.to(Vec2::new(cx, cy - 120.0), MORPH),
811 s3_g_end.position.to(Vec2::new(cx, cy + 120.0), MORPH),
812 s3_g_line.start.to(Vec2::new(0.0, -120.0), MORPH),
813 s3_g_line.end.to(Vec2::new(0.0, 120.0), MORPH),
814 ],
815 wait!(1),
816 // Direction morph: vertical ocean -> horizontal ocean
817 all![
818 s3.stroke_paint.to(Paint::Gradient(ocean.clone()), MORPH),
819 s3_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
820 s3_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
821 s3_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
822 s3_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
823 ],
824 wait!(1),
825 all![
826 s3_lbl.opacity.to(0.0, FADE_OUT),
827 s3.opacity.to(0.0, FADE_OUT),
828 s3.scale.to(Vec2::splat(0.95), FADE_OUT),
829 s3_g_start.opacity.to(0.0, FADE_OUT),
830 s3_g_end.opacity.to(0.0, FADE_OUT),
831 s3_g_line.opacity.to(0.0, FADE_OUT),
832 ],
833 // ─── 4: Line gradient stroke ───
834 all![
835 s4_lbl.opacity.to(1.0, FADE_IN),
836 s4.opacity.to(1.0, FADE_IN),
837 s4_g_start.opacity.to(1.0, FADE_IN),
838 s4_g_end.opacity.to(1.0, FADE_IN),
839 s4_g_line.opacity.to(1.0, FADE_IN),
840 ],
841 wait!(1),
842 // Morph: sunset (diagonal) -> ocean (horizontal)
843 all![
844 s4.stroke_paint.to(Paint::Gradient(ocean.clone()), MORPH),
845 s4_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
846 s4_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
847 s4_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
848 s4_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
849 s4_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
850 s4_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
851 ],
852 wait!(1),
853 // Morph: ocean (horizontal) -> sunset (diagonal)
854 all![
855 s4.stroke_paint.to(Paint::Gradient(sunset.clone()), MORPH),
856 s4_g_start
857 .position
858 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
859 s4_g_end
860 .position
861 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
862 s4_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
863 s4_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
864 s4_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
865 s4_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
866 ],
867 wait!(1),
868 all![
869 s4_lbl.opacity.to(0.0, FADE_OUT),
870 s4.opacity.to(0.0, FADE_OUT),
871 s4_g_start.opacity.to(0.0, FADE_OUT),
872 s4_g_end.opacity.to(0.0, FADE_OUT),
873 s4_g_line.opacity.to(0.0, FADE_OUT),
874 ],
875 // ─── 5: Path gradient stroke ───
876 all![
877 s5_lbl.opacity.to(1.0, FADE_IN),
878 s5.opacity.to(1.0, FADE_IN),
879 s5_g_start.opacity.to(1.0, FADE_IN),
880 s5_g_end.opacity.to(1.0, FADE_IN),
881 s5_g_line.opacity.to(1.0, FADE_IN),
882 ],
883 wait!(1),
884 // Morph: ocean (horizontal) -> sunset (diagonal)
885 all![
886 s5.stroke_paint.to(Paint::Gradient(sunset.clone()), MORPH),
887 s5_g_start
888 .position
889 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
890 s5_g_end
891 .position
892 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
893 s5_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
894 s5_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
895 s5_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
896 s5_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
897 ],
898 wait!(1),
899 // Morph: sunset (diagonal) -> ocean (horizontal)
900 all![
901 s5.stroke_paint.to(Paint::Gradient(ocean.clone()), MORPH),
902 s5_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
903 s5_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
904 s5_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
905 s5_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
906 s5_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
907 s5_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
908 ],
909 wait!(1),
910 all![
911 s5_lbl.opacity.to(0.0, FADE_OUT),
912 s5.opacity.to(0.0, FADE_OUT),
913 s5_g_start.opacity.to(0.0, FADE_OUT),
914 s5_g_end.opacity.to(0.0, FADE_OUT),
915 s5_g_line.opacity.to(0.0, FADE_OUT),
916 ],
917 // ─── 6: Grid gradient stroke ───
918 all![
919 s6_lbl.opacity.to(1.0, FADE_IN),
920 s6_grid.opacity.to(1.0, FADE_IN),
921 s6_g_start.opacity.to(1.0, FADE_IN),
922 s6_g_end.opacity.to(1.0, FADE_IN),
923 s6_g_line.opacity.to(1.0, FADE_IN),
924 ],
925 wait!(1),
926 // Morph: ocean (horizontal) -> sunset (diagonal)
927 all![
928 s6_grid
929 .stroke_paint
930 .to(Paint::Gradient(sunset.clone()), MORPH),
931 s6_g_start
932 .position
933 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
934 s6_g_end
935 .position
936 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
937 s6_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
938 s6_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
939 s6_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
940 s6_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
941 ],
942 wait!(1),
943 // Morph: sunset (diagonal) -> ocean (horizontal)
944 all![
945 s6_grid
946 .stroke_paint
947 .to(Paint::Gradient(ocean.clone()), MORPH),
948 s6_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
949 s6_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
950 s6_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
951 s6_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
952 s6_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
953 s6_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
954 ],
955 wait!(1),
956 all![
957 s6_lbl.opacity.to(0.0, FADE_OUT),
958 s6_grid.opacity.to(0.0, FADE_OUT),
959 s6_g_start.opacity.to(0.0, FADE_OUT),
960 s6_g_end.opacity.to(0.0, FADE_OUT),
961 s6_g_line.opacity.to(0.0, FADE_OUT),
962 ],
963 // ─── 7: Text — morph colors, then direction ───
964 all![
965 s7_lbl.opacity.to(1.0, FADE_IN),
966 s7.opacity.to(1.0, FADE_IN),
967 s7.scale.to(Vec2::ONE, FADE_IN),
968 s7_g_start.opacity.to(1.0, FADE_IN),
969 s7_g_end.opacity.to(1.0, FADE_IN),
970 s7_g_line.opacity.to(1.0, FADE_IN),
971 ],
972 wait!(1),
973 // Morph: ocean (horizontal) -> sunset (diagonal)
974 all![
975 s7.fill_paint.to(Paint::Gradient(sunset.clone()), MORPH),
976 s7_g_start
977 .position
978 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
979 s7_g_end
980 .position
981 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
982 s7_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
983 s7_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
984 s7_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
985 s7_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
986 ],
987 wait!(1),
988 // Morph: sunset (diagonal) -> ocean (horizontal)
989 all![
990 s7.fill_paint.to(Paint::Gradient(ocean.clone()), MORPH),
991 s7_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
992 s7_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
993 s7_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
994 s7_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
995 s7_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
996 s7_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
997 ],
998 wait!(1),
999 // Morph: horizontal ocean -> vertical ocean
1000 all![
1001 s7.fill_paint.to(Paint::Gradient(ocean_vert.clone()), MORPH),
1002 s7_g_start.position.to(Vec2::new(cx, cy - 120.0), MORPH),
1003 s7_g_end.position.to(Vec2::new(cx, cy + 120.0), MORPH),
1004 s7_g_line.start.to(Vec2::new(0.0, -120.0), MORPH),
1005 s7_g_line.end.to(Vec2::new(0.0, 120.0), MORPH),
1006 ],
1007 wait!(1),
1008 // Morph: vertical ocean -> horizontal ocean
1009 all![
1010 s7.fill_paint.to(Paint::Gradient(ocean.clone()), MORPH),
1011 s7_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
1012 s7_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
1013 s7_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
1014 s7_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
1015 ],
1016 wait!(1),
1017 all![
1018 s7_lbl.opacity.to(0.0, FADE_OUT),
1019 s7.opacity.to(0.0, FADE_OUT),
1020 s7.scale.to(Vec2::splat(0.95), FADE_OUT),
1021 s7_g_start.opacity.to(0.0, FADE_OUT),
1022 s7_g_end.opacity.to(0.0, FADE_OUT),
1023 s7_g_line.opacity.to(0.0, FADE_OUT),
1024 ],
1025 // ─── 8: Math ───
1026 all![
1027 s8_lbl.opacity.to(1.0, FADE_IN),
1028 s8.opacity.to(1.0, FADE_IN),
1029 s8.scale.to(Vec2::ONE, FADE_IN),
1030 s8_g_start.opacity.to(1.0, FADE_IN),
1031 s8_g_end.opacity.to(1.0, FADE_IN),
1032 s8_g_line.opacity.to(1.0, FADE_IN),
1033 ],
1034 wait!(1),
1035 // Morph: sunset (diagonal) -> ocean (horizontal)
1036 all![
1037 s8.fill_paint.to(Paint::Gradient(ocean.clone()), MORPH),
1038 s8_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
1039 s8_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
1040 s8_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
1041 s8_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
1042 s8_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
1043 s8_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
1044 ],
1045 wait!(1),
1046 // Morph: ocean (horizontal) -> sunset (diagonal)
1047 all![
1048 s8.fill_paint.to(Paint::Gradient(sunset.clone()), MORPH),
1049 s8_g_start
1050 .position
1051 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
1052 s8_g_end
1053 .position
1054 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
1055 s8_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
1056 s8_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
1057 s8_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
1058 s8_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
1059 ],
1060 wait!(1),
1061 all![
1062 s8_lbl.opacity.to(0.0, FADE_OUT),
1063 s8.opacity.to(0.0, FADE_OUT),
1064 s8.scale.to(Vec2::splat(0.95), FADE_OUT),
1065 s8_g_start.opacity.to(0.0, FADE_OUT),
1066 s8_g_end.opacity.to(0.0, FADE_OUT),
1067 s8_g_line.opacity.to(0.0, FADE_OUT),
1068 ],
1069 // ─── 9: Solid ↔ Gradient ───
1070 all![
1071 s9_lbl.opacity.to(1.0, FADE_IN),
1072 s9.opacity.to(1.0, FADE_IN),
1073 s9.scale.to(Vec2::ONE, FADE_IN),
1074 ],
1075 wait!(1),
1076 // Morph: solid white -> sunset (diagonal gradient) + fade in guides
1077 all![
1078 s9.fill_paint.to(Paint::Gradient(sunset.clone()), MORPH),
1079 s9_g_start.opacity.to(1.0, MORPH),
1080 s9_g_end.opacity.to(1.0, MORPH),
1081 s9_g_line.opacity.to(1.0, MORPH),
1082 ],
1083 wait!(1),
1084 // Morph: sunset -> solid white + fade out guides
1085 all![
1086 s9.fill_paint.to(Paint::Solid(Color::WHITE), MORPH),
1087 s9_g_start.opacity.to(0.0, MORPH),
1088 s9_g_end.opacity.to(0.0, MORPH),
1089 s9_g_line.opacity.to(0.0, MORPH),
1090 ],
1091 wait!(1),
1092 all![
1093 s9_lbl.opacity.to(0.0, FADE_OUT),
1094 s9.opacity.to(0.0, FADE_OUT),
1095 s9.scale.to(Vec2::splat(0.95), FADE_OUT),
1096 ],
1097 // ─── 10: Radial ↔ Linear Morph ───
1098 all![
1099 s10_lbl.opacity.to(1.0, FADE_IN),
1100 s10.opacity.to(1.0, FADE_IN),
1101 s10.scale.to(Vec2::ONE, FADE_IN),
1102 s10_g_rad_center.opacity.to(1.0, FADE_IN),
1103 s10_g_rad_radius.opacity.to(1.0, FADE_IN),
1104 ],
1105 wait!(1),
1106 // Morph Radial (glow) to Linear (sunset) + cross-fade the visual guides
1107 all![
1108 s10.fill_paint.to(Paint::Gradient(sunset.clone()), MORPH),
1109 s10_g_rad_center.opacity.to(0.0, MORPH),
1110 s10_g_rad_radius.opacity.to(0.0, MORPH),
1111 s10_g_lin_start.opacity.to(1.0, MORPH),
1112 s10_g_lin_end.opacity.to(1.0, MORPH),
1113 s10_g_lin_line.opacity.to(1.0, MORPH),
1114 ],
1115 wait!(1),
1116 // Morph back from Linear (sunset) to Radial (glow)
1117 all![
1118 s10.fill_paint.to(Paint::Gradient(glow.clone()), MORPH),
1119 s10_g_rad_center.opacity.to(1.0, MORPH),
1120 s10_g_rad_radius.opacity.to(1.0, MORPH),
1121 s10_g_lin_start.opacity.to(0.0, MORPH),
1122 s10_g_lin_end.opacity.to(0.0, MORPH),
1123 s10_g_lin_line.opacity.to(0.0, MORPH),
1124 ],
1125 wait!(1),
1126 // Fade everything out
1127 all![
1128 s10_lbl.opacity.to(0.0, FADE_OUT),
1129 s10.opacity.to(0.0, FADE_OUT),
1130 s10_g_rad_center.opacity.to(0.0, FADE_OUT),
1131 s10_g_rad_radius.opacity.to(0.0, FADE_OUT),
1132 title.opacity.to(0.0, FADE_OUT),
1133 grid.opacity.to(0.0, FADE_OUT),
1134 ],
1135 ]);
1136
1137 project.show().expect("Failed to render animation");
1138}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(&tex);
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
- examples/mask_demo.rs
- examples/polygon.rs
- examples/export.rs
- examples/audio_demo.rs
- examples/shapes.rs
- examples/camera_demo.rs
- examples/nested_cameras.rs
- examples/math_code.rs
- examples/easing_scope.rs
- examples/signals.rs
- examples/blur_demo.rs
- examples/advanced_flow.rs
- examples/anchors.rs
Sourcepub const fn rgba8(r: u8, g: u8, b: u8, a: u8) -> Color
pub const fn rgba8(r: u8, g: u8, b: u8, a: u8) -> Color
Creates a new RGBA color.
Examples found in repository?
87const GRID_COLOR: Color = Color::rgba8(0x20, 0x20, 0x38, 60);
88const LABEL: Color = Color::rgb8(0x99, 0x99, 0xbb);
89
90// Timing
91const FADE_IN: Duration = Duration::from_millis(500);
92const FADE_OUT: Duration = Duration::from_millis(300);
93const MORPH: Duration = Duration::from_millis(1800);
94
95fn main() {
96 let cx = 960.0_f32;
97 let cy = 480.0_f32;
98
99 let mut project = Project::default()
100 .with_dimensions(1920, 1080)
101 .with_cache(true)
102 .with_title("Gradient Demo")
103 .close_on_finish();
104
105 // Background
106 let bg_rect = Rect::default()
107 .with_position(Vec2::new(cx, 540.0))
108 .with_size(Vec2::new(1920.0, 1080.0))
109 .with_fill(BG);
110
111 let grid = GridNode::default()
112 .with_position(Vec2::new(cx, 540.0))
113 .with_columns(32.0)
114 .with_rows(18.0)
115 .with_spacing_all(60.0)
116 .with_stroke(GRID_COLOR, 1.0)
117 .with_opacity(0.0);
118
119 // Gradients
120 let sunset = tri_grad(CORAL, PINK, VIOLET, 120.0);
121 let ocean = linear_grad(CYAN, INDIGO, 120.0);
122 let glow = radial_grad(FUCHSIA, Color::rgba8(0x4f, 0x46, 0xe5, 0), 130.0);
123 let glow_alt = radial_grad(CYAN, Color::rgba8(0x00, 0xf2, 0xfe, 0), 180.0);
124
125 // Sunset in different directions (same colors, different orientation)
126 let sunset_stops = vec![
127 ColorStop {
128 offset: 0.0,
129 color: CORAL,
130 },
131 ColorStop {
132 offset: 0.5,
133 color: PINK,
134 },
135 ColorStop {
136 offset: 1.0,
137 color: VIOLET,
138 },
139 ];
140 let sunset_horiz = directed_grad(
141 Point::new(-120.0, 0.0),
142 Point::new(120.0, 0.0),
143 sunset_stops.clone(),
144 );
145 let sunset_vert = directed_grad(
146 Point::new(0.0, -120.0),
147 Point::new(0.0, 120.0),
148 sunset_stops.clone(),
149 );
150 let sunset_diag = directed_grad(
151 Point::new(-120.0, -120.0),
152 Point::new(120.0, 120.0),
153 sunset_stops.clone(),
154 );
155
156 // Ocean in different directions
157 let ocean_stops = vec![
158 ColorStop {
159 offset: 0.0,
160 color: CYAN,
161 },
162 ColorStop {
163 offset: 1.0,
164 color: INDIGO,
165 },
166 ];
167 let ocean_vert = directed_grad(
168 Point::new(0.0, -120.0),
169 Point::new(0.0, 120.0),
170 ocean_stops.clone(),
171 );
172
173 // Persistent title
174 let title = TextNode::default()
175 .with_position(Vec2::new(cx, 100.0))
176 .with_text("Gradient Paint Demo")
177 .with_font_size(48.0)
178 .with_fill(Color::rgb8(0xdd, 0xdd, 0xee))
179 .with_opacity(0.0);
180
181 // Per-scene label (reused position, swapped text via separate nodes)
182 let label_y = 200.0;
183
184 // ─── SCENE 1: Circle with linear gradient fill ───
185 let s1_lbl = TextNode::default()
186 .with_position(Vec2::new(cx, label_y))
187 .with_text("Linear Gradient Fill")
188 .with_font_size(28.0)
189 .with_fill(LABEL)
190 .with_opacity(0.0);
191
192 let s1 = Circle::default()
193 .with_position(Vec2::new(cx, cy))
194 .with_radius(130.0)
195 .with_fill(sunset.clone())
196 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 20), 2.0)
197 .with_scale(0.95)
198 .with_opacity(0.0);
199
200 // Guides
201 let s1_g_start = Circle::default()
202 .with_position(Vec2::new(cx - 120.0, cy - 120.0))
203 .with_radius(8.0)
204 .with_fill(Color::WHITE)
205 .with_stroke(CORAL, 2.5)
206 .with_opacity(0.0);
207
208 let s1_g_end = Circle::default()
209 .with_position(Vec2::new(cx + 120.0, cy + 120.0))
210 .with_radius(8.0)
211 .with_fill(Color::WHITE)
212 .with_stroke(VIOLET, 2.5)
213 .with_opacity(0.0);
214
215 let s1_g_line = Line::default()
216 .with_position(Vec2::new(cx, cy))
217 .with_start(Vec2::new(-120.0, -120.0))
218 .with_end(Vec2::new(120.0, 120.0))
219 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
220 .with_opacity(0.0);
221
222 // ─── SCENE 2: Rect with radial gradient fill ───
223 let s2_lbl = TextNode::default()
224 .with_position(Vec2::new(cx, label_y))
225 .with_text("Radial Gradient Fill")
226 .with_font_size(28.0)
227 .with_fill(LABEL)
228 .with_opacity(0.0);
229
230 let s2 = Rect::default()
231 .with_position(Vec2::new(cx, cy))
232 .with_size(Vec2::new(260.0, 260.0))
233 .with_radius(24.0)
234 .with_fill(glow.clone())
235 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 20), 2.0)
236 .with_scale(0.95)
237 .with_opacity(0.0);
238
239 // Guides
240 let s2_g_center = Circle::default()
241 .with_position(Vec2::new(cx, cy))
242 .with_radius(8.0)
243 .with_fill(Color::WHITE)
244 .with_stroke(FUCHSIA, 2.5)
245 .with_opacity(0.0);
246
247 let s2_g_radius = Circle::default()
248 .with_position(Vec2::new(cx, cy))
249 .with_radius(130.0)
250 .with_fill(Color::TRANSPARENT)
251 .with_stroke(Color::rgba8(0xff, 0x00, 0x7f, 80), 1.5)
252 .with_opacity(0.0);
253
254 // ─── SCENE 3: Hexagon with gradient stroke ───
255 let s3_lbl = TextNode::default()
256 .with_position(Vec2::new(cx, label_y))
257 .with_text("Gradient Stroke")
258 .with_font_size(28.0)
259 .with_fill(LABEL)
260 .with_opacity(0.0);
261
262 let s3 = Polygon::regular(6, 130.0)
263 .with_position(Vec2::new(cx, cy))
264 .with_fill(Color::rgba8(0x12, 0x12, 0x20, 255))
265 .with_stroke(ocean.clone(), 5.0)
266 .with_scale(0.95)
267 .with_opacity(0.0);
268
269 // Guides
270 let s3_g_start = Circle::default()
271 .with_position(Vec2::new(cx - 120.0, cy))
272 .with_radius(8.0)
273 .with_fill(Color::WHITE)
274 .with_stroke(CYAN, 2.5)
275 .with_opacity(0.0);
276
277 let s3_g_end = Circle::default()
278 .with_position(Vec2::new(cx + 120.0, cy))
279 .with_radius(8.0)
280 .with_fill(Color::WHITE)
281 .with_stroke(INDIGO, 2.5)
282 .with_opacity(0.0);
283
284 let s3_g_line = Line::default()
285 .with_position(Vec2::new(cx, cy))
286 .with_start(Vec2::new(-120.0, 0.0))
287 .with_end(Vec2::new(120.0, 0.0))
288 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
289 .with_opacity(0.0);
290
291 // ─── SCENE 4: Line with gradient stroke ───
292 let s4_lbl = TextNode::default()
293 .with_position(Vec2::new(cx, label_y))
294 .with_text("Gradient Line")
295 .with_font_size(28.0)
296 .with_fill(LABEL)
297 .with_opacity(0.0);
298
299 let s4 = Line::default()
300 .with_position(Vec2::new(cx, cy))
301 .with_start(Vec2::new(-160.0, 100.0))
302 .with_end(Vec2::new(160.0, -100.0))
303 .with_stroke(sunset.clone(), 8.0)
304 .with_opacity(0.0);
305
306 // Guides
307 let s4_g_start = Circle::default()
308 .with_position(Vec2::new(cx - 120.0, cy - 120.0))
309 .with_radius(8.0)
310 .with_fill(Color::WHITE)
311 .with_stroke(CORAL, 2.5)
312 .with_opacity(0.0);
313
314 let s4_g_end = Circle::default()
315 .with_position(Vec2::new(cx + 120.0, cy + 120.0))
316 .with_radius(8.0)
317 .with_fill(Color::WHITE)
318 .with_stroke(VIOLET, 2.5)
319 .with_opacity(0.0);
320
321 let s4_g_line = Line::default()
322 .with_position(Vec2::new(cx, cy))
323 .with_start(Vec2::new(-120.0, -120.0))
324 .with_end(Vec2::new(120.0, 120.0))
325 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
326 .with_opacity(0.0);
327
328 // ─── SCENE 5: Path with gradient stroke ───
329 let s5_lbl = TextNode::default()
330 .with_position(Vec2::new(cx, label_y))
331 .with_text("Gradient Path")
332 .with_font_size(28.0)
333 .with_fill(LABEL)
334 .with_opacity(0.0);
335
336 let mut tri_path = BezPath::new();
337 tri_path.move_to(kurbo::Point::new(0.0, -120.0));
338 tri_path.line_to(kurbo::Point::new(104.0, 60.0));
339 tri_path.line_to(kurbo::Point::new(-104.0, 60.0));
340 tri_path.close_path();
341
342 let s5 = PathNode::new(Vec2::new(cx, cy), tri_path, CYAN, 6.0)
343 .with_stroke(ocean.clone(), 6.0)
344 .with_opacity(0.0);
345
346 // Guides
347 let s5_g_start = Circle::default()
348 .with_position(Vec2::new(cx - 120.0, cy))
349 .with_radius(8.0)
350 .with_fill(Color::WHITE)
351 .with_stroke(CYAN, 2.5)
352 .with_opacity(0.0);
353
354 let s5_g_end = Circle::default()
355 .with_position(Vec2::new(cx + 120.0, cy))
356 .with_radius(8.0)
357 .with_fill(Color::WHITE)
358 .with_stroke(INDIGO, 2.5)
359 .with_opacity(0.0);
360
361 let s5_g_line = Line::default()
362 .with_position(Vec2::new(cx, cy))
363 .with_start(Vec2::new(-120.0, 0.0))
364 .with_end(Vec2::new(120.0, 0.0))
365 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
366 .with_opacity(0.0);
367
368 // ─── SCENE 6: Grid with gradient stroke ───
369 let s6_lbl = TextNode::default()
370 .with_position(Vec2::new(cx, label_y))
371 .with_text("Gradient Grid")
372 .with_font_size(28.0)
373 .with_fill(LABEL)
374 .with_opacity(0.0);
375
376 let s6_grid = GridNode::default()
377 .with_position(Vec2::new(cx, cy))
378 .with_columns(8.0)
379 .with_rows(6.0)
380 .with_spacing_all(50.0)
381 .with_stroke(ocean.clone(), 2.0)
382 .with_opacity(0.0);
383
384 // Guides
385 let s6_g_start = Circle::default()
386 .with_position(Vec2::new(cx - 120.0, cy))
387 .with_radius(8.0)
388 .with_fill(Color::WHITE)
389 .with_stroke(CYAN, 2.5)
390 .with_opacity(0.0);
391
392 let s6_g_end = Circle::default()
393 .with_position(Vec2::new(cx + 120.0, cy))
394 .with_radius(8.0)
395 .with_fill(Color::WHITE)
396 .with_stroke(INDIGO, 2.5)
397 .with_opacity(0.0);
398
399 let s6_g_line = Line::default()
400 .with_position(Vec2::new(cx, cy))
401 .with_start(Vec2::new(-120.0, 0.0))
402 .with_end(Vec2::new(120.0, 0.0))
403 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
404 .with_opacity(0.0);
405
406 // ─── SCENE 7: Text with gradient fill ───
407 let s7_lbl = TextNode::default()
408 .with_position(Vec2::new(cx, label_y))
409 .with_text("Gradient Text")
410 .with_font_size(28.0)
411 .with_fill(LABEL)
412 .with_opacity(0.0);
413
414 let s7 = TextNode::default()
415 .with_position(Vec2::new(cx, cy))
416 .with_text("HELLO GRADIENTS")
417 .with_font_size(80.0)
418 .with_fill(ocean.clone())
419 .with_scale(0.95)
420 .with_opacity(0.0);
421
422 // Guides
423 let s7_g_start = Circle::default()
424 .with_position(Vec2::new(cx - 120.0, cy))
425 .with_radius(8.0)
426 .with_fill(Color::WHITE)
427 .with_stroke(CYAN, 2.5)
428 .with_opacity(0.0);
429
430 let s7_g_end = Circle::default()
431 .with_position(Vec2::new(cx + 120.0, cy))
432 .with_radius(8.0)
433 .with_fill(Color::WHITE)
434 .with_stroke(INDIGO, 2.5)
435 .with_opacity(0.0);
436
437 let s7_g_line = Line::default()
438 .with_position(Vec2::new(cx, cy))
439 .with_start(Vec2::new(-120.0, 0.0))
440 .with_end(Vec2::new(120.0, 0.0))
441 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
442 .with_opacity(0.0);
443
444 // ─── SCENE 8: Math with gradient fill ───
445 let s8_lbl = TextNode::default()
446 .with_position(Vec2::new(cx, label_y))
447 .with_text("Gradient Math")
448 .with_font_size(28.0)
449 .with_fill(LABEL)
450 .with_opacity(0.0);
451
452 let s8 = MathNode::default()
453 .with_position(Vec2::new(cx, cy))
454 .with_equation("E = m c^2")
455 .with_font_size(72.0)
456 .with_fill(sunset.clone())
457 .with_scale(0.95)
458 .with_opacity(0.0);
459
460 // Guides
461 let s8_g_start = Circle::default()
462 .with_position(Vec2::new(cx - 120.0, cy - 120.0))
463 .with_radius(8.0)
464 .with_fill(Color::WHITE)
465 .with_stroke(CORAL, 2.5)
466 .with_opacity(0.0);
467
468 let s8_g_end = Circle::default()
469 .with_position(Vec2::new(cx + 120.0, cy + 120.0))
470 .with_radius(8.0)
471 .with_fill(Color::WHITE)
472 .with_stroke(VIOLET, 2.5)
473 .with_opacity(0.0);
474
475 let s8_g_line = Line::default()
476 .with_position(Vec2::new(cx, cy))
477 .with_start(Vec2::new(-120.0, -120.0))
478 .with_end(Vec2::new(120.0, 120.0))
479 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
480 .with_opacity(0.0);
481
482 // ─── SCENE 9: Solid ↔ Gradient ───
483 let s9_lbl = TextNode::default()
484 .with_position(Vec2::new(cx, label_y))
485 .with_text("Solid to Gradient")
486 .with_font_size(28.0)
487 .with_fill(LABEL)
488 .with_opacity(0.0);
489
490 let s9 = Circle::default()
491 .with_position(Vec2::new(cx, cy))
492 .with_radius(130.0)
493 .with_fill(Color::WHITE)
494 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 20), 2.0)
495 .with_scale(0.95)
496 .with_opacity(0.0);
497
498 // Guides (start with opacity 0, fade in when Sunset is active, fade out on Solid)
499 let s9_g_start = Circle::default()
500 .with_position(Vec2::new(cx - 120.0, cy - 120.0))
501 .with_radius(8.0)
502 .with_fill(Color::WHITE)
503 .with_stroke(CORAL, 2.5)
504 .with_opacity(0.0);
505
506 let s9_g_end = Circle::default()
507 .with_position(Vec2::new(cx + 120.0, cy + 120.0))
508 .with_radius(8.0)
509 .with_fill(Color::WHITE)
510 .with_stroke(VIOLET, 2.5)
511 .with_opacity(0.0);
512
513 let s9_g_line = Line::default()
514 .with_position(Vec2::new(cx, cy))
515 .with_start(Vec2::new(-120.0, -120.0))
516 .with_end(Vec2::new(120.0, 120.0))
517 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
518 .with_opacity(0.0);
519
520 // ─── SCENE 10: Radial ↔ Linear Interpolation ───
521 let s10_lbl = TextNode::default()
522 .with_position(Vec2::new(cx, label_y))
523 .with_text("Radial ↔ Linear Morph")
524 .with_font_size(28.0)
525 .with_fill(LABEL)
526 .with_opacity(0.0);
527
528 let s10 = Circle::default()
529 .with_position(Vec2::new(cx, cy))
530 .with_radius(130.0)
531 .with_fill(glow.clone()) // Start as Radial Gradient
532 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 20), 2.0)
533 .with_scale(0.95)
534 .with_opacity(0.0);
535
536 // Visual guides for Scene 10 (cross-fading between radial and linear handles)
537 let s10_g_rad_center = Circle::default()
538 .with_position(Vec2::new(cx, cy))
539 .with_radius(8.0)
540 .with_fill(Color::WHITE)
541 .with_stroke(FUCHSIA, 2.5)
542 .with_opacity(0.0);
543
544 let s10_g_rad_radius = Circle::default()
545 .with_position(Vec2::new(cx, cy))
546 .with_radius(130.0)
547 .with_fill(Color::TRANSPARENT)
548 .with_stroke(Color::rgba8(0xff, 0x00, 0x7f, 80), 1.5)
549 .with_opacity(0.0);
550
551 let s10_g_lin_start = Circle::default()
552 .with_position(Vec2::new(cx - 120.0, cy - 120.0))
553 .with_radius(8.0)
554 .with_fill(Color::WHITE)
555 .with_stroke(CORAL, 2.5)
556 .with_opacity(0.0);
557
558 let s10_g_lin_end = Circle::default()
559 .with_position(Vec2::new(cx + 120.0, cy + 120.0))
560 .with_radius(8.0)
561 .with_fill(Color::WHITE)
562 .with_stroke(VIOLET, 2.5)
563 .with_opacity(0.0);
564
565 let s10_g_lin_line = Line::default()
566 .with_position(Vec2::new(cx, cy))
567 .with_start(Vec2::new(-120.0, -120.0))
568 .with_end(Vec2::new(120.0, 120.0))
569 .with_stroke(Color::rgba8(0xff, 0xff, 0xff, 80), 1.5)
570 .with_opacity(0.0);
571
572 // Add all to scene
573 project.scene.add(&bg_rect);
574 project.scene.add(&grid);
575 project.scene.add(&title);
576
577 // Scene 1
578 project.scene.add(&s1_lbl);
579 project.scene.add(&s1);
580 project.scene.add(&s1_g_line);
581 project.scene.add(&s1_g_start);
582 project.scene.add(&s1_g_end);
583
584 // Scene 2
585 project.scene.add(&s2_lbl);
586 project.scene.add(&s2);
587 project.scene.add(&s2_g_radius);
588 project.scene.add(&s2_g_center);
589
590 // Scene 3
591 project.scene.add(&s3_lbl);
592 project.scene.add(&s3);
593 project.scene.add(&s3_g_line);
594 project.scene.add(&s3_g_start);
595 project.scene.add(&s3_g_end);
596
597 // Scene 4
598 project.scene.add(&s4_lbl);
599 project.scene.add(&s4);
600 project.scene.add(&s4_g_line);
601 project.scene.add(&s4_g_start);
602 project.scene.add(&s4_g_end);
603
604 // Scene 5
605 project.scene.add(&s5_lbl);
606 project.scene.add(&s5);
607 project.scene.add(&s5_g_line);
608 project.scene.add(&s5_g_start);
609 project.scene.add(&s5_g_end);
610
611 // Scene 6
612 project.scene.add(&s6_lbl);
613 project.scene.add(&s6_grid);
614 project.scene.add(&s6_g_line);
615 project.scene.add(&s6_g_start);
616 project.scene.add(&s6_g_end);
617
618 // Scene 7
619 project.scene.add(&s7_lbl);
620 project.scene.add(&s7);
621 project.scene.add(&s7_g_line);
622 project.scene.add(&s7_g_start);
623 project.scene.add(&s7_g_end);
624
625 // Scene 8
626 project.scene.add(&s8_lbl);
627 project.scene.add(&s8);
628 project.scene.add(&s8_g_line);
629 project.scene.add(&s8_g_start);
630 project.scene.add(&s8_g_end);
631
632 // Scene 9
633 project.scene.add(&s9_lbl);
634 project.scene.add(&s9);
635 project.scene.add(&s9_g_line);
636 project.scene.add(&s9_g_start);
637 project.scene.add(&s9_g_end);
638
639 // Scene 10
640 project.scene.add(&s10_lbl);
641 project.scene.add(&s10);
642 project.scene.add(&s10_g_rad_radius);
643 project.scene.add(&s10_g_rad_center);
644 project.scene.add(&s10_g_lin_line);
645 project.scene.add(&s10_g_lin_start);
646 project.scene.add(&s10_g_lin_end);
647
648 // Timeline — one scene at a time, each with its own gradient animation
649 project.scene.video_timeline.add(chain![
650 // Intro
651 all![
652 grid.opacity.to(1.0, Duration::from_millis(800)),
653 title.opacity.to(1.0, Duration::from_millis(800)),
654 ],
655 wait!(1),
656 // ─── 1: Circle linear fill — morph colors, then direction ───
657 all![
658 s1_lbl.opacity.to(1.0, FADE_IN),
659 s1.opacity.to(1.0, FADE_IN),
660 s1.scale.to(Vec2::ONE, FADE_IN),
661 s1_g_start.opacity.to(1.0, FADE_IN),
662 s1_g_end.opacity.to(1.0, FADE_IN),
663 s1_g_line.opacity.to(1.0, FADE_IN),
664 ],
665 wait!(1),
666 // Color morph: sunset → ocean
667 all![
668 s1.fill_paint.to(Paint::Gradient(ocean.clone()), MORPH),
669 s1_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
670 s1_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
671 s1_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
672 s1_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
673 s1_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
674 s1_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
675 ],
676 wait!(1),
677 // Color morph: ocean → sunset
678 all![
679 s1.fill_paint.to(Paint::Gradient(sunset.clone()), MORPH),
680 s1_g_start
681 .position
682 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
683 s1_g_end
684 .position
685 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
686 s1_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
687 s1_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
688 s1_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
689 s1_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
690 ],
691 wait!(1),
692 // Direction morph: diagonal → horizontal sunset
693 all![
694 s1.fill_paint
695 .to(Paint::Gradient(sunset_horiz.clone()), MORPH),
696 s1_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
697 s1_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
698 s1_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
699 s1_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
700 ],
701 wait!(1),
702 // Direction morph: horizontal → vertical sunset
703 all![
704 s1.fill_paint
705 .to(Paint::Gradient(sunset_vert.clone()), MORPH),
706 s1_g_start.position.to(Vec2::new(cx, cy - 120.0), MORPH),
707 s1_g_end.position.to(Vec2::new(cx, cy + 120.0), MORPH),
708 s1_g_line.start.to(Vec2::new(0.0, -120.0), MORPH),
709 s1_g_line.end.to(Vec2::new(0.0, 120.0), MORPH),
710 ],
711 wait!(1),
712 // Direction morph: vertical → diagonal sunset
713 all![
714 s1.fill_paint
715 .to(Paint::Gradient(sunset_diag.clone()), MORPH),
716 s1_g_start
717 .position
718 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
719 s1_g_end
720 .position
721 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
722 s1_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
723 s1_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
724 ],
725 wait!(1),
726 all![
727 s1_lbl.opacity.to(0.0, FADE_OUT),
728 s1.opacity.to(0.0, FADE_OUT),
729 s1.scale.to(Vec2::splat(0.95), FADE_OUT),
730 s1_g_start.opacity.to(0.0, FADE_OUT),
731 s1_g_end.opacity.to(0.0, FADE_OUT),
732 s1_g_line.opacity.to(0.0, FADE_OUT),
733 ],
734 // ─── 2: Rect radial fill — morph to another radial gradient and back ───
735 all![
736 s2_lbl.opacity.to(1.0, FADE_IN),
737 s2.opacity.to(1.0, FADE_IN),
738 s2.scale.to(Vec2::ONE, FADE_IN),
739 s2_g_center.opacity.to(1.0, FADE_IN),
740 s2_g_radius.opacity.to(1.0, FADE_IN),
741 ],
742 wait!(1),
743 // Radial color and radius morph: glow (Fuchsia 130px) -> glow_alt (Cyan 180px)
744 all![
745 s2.fill_paint.to(Paint::Gradient(glow_alt.clone()), MORPH),
746 s2_g_radius.radius.to(180.0, MORPH),
747 s2_g_center.stroke_paint.to(Paint::Solid(CYAN), MORPH),
748 s2_g_radius
749 .stroke_paint
750 .to(Paint::Solid(Color::rgba8(0x00, 0xf2, 0xfe, 80)), MORPH),
751 ],
752 wait!(1),
753 // Morph back to glow
754 all![
755 s2.fill_paint.to(Paint::Gradient(glow.clone()), MORPH),
756 s2_g_radius.radius.to(130.0, MORPH),
757 s2_g_center.stroke_paint.to(Paint::Solid(FUCHSIA), MORPH),
758 s2_g_radius
759 .stroke_paint
760 .to(Paint::Solid(Color::rgba8(0xff, 0x00, 0x7f, 80)), MORPH),
761 ],
762 wait!(1),
763 all![
764 s2_lbl.opacity.to(0.0, FADE_OUT),
765 s2.opacity.to(0.0, FADE_OUT),
766 s2.scale.to(Vec2::splat(0.95), FADE_OUT),
767 s2_g_center.opacity.to(0.0, FADE_OUT),
768 s2_g_radius.opacity.to(0.0, FADE_OUT),
769 ],
770 // ─── 3: Hex gradient stroke — morph colors, then direction ───
771 all![
772 s3_lbl.opacity.to(1.0, FADE_IN),
773 s3.opacity.to(1.0, FADE_IN),
774 s3.scale.to(Vec2::ONE, FADE_IN),
775 s3_g_start.opacity.to(1.0, FADE_IN),
776 s3_g_end.opacity.to(1.0, FADE_IN),
777 s3_g_line.opacity.to(1.0, FADE_IN),
778 ],
779 wait!(1),
780 // Morph: ocean (horizontal) -> sunset (diagonal)
781 all![
782 s3.stroke_paint.to(Paint::Gradient(sunset.clone()), MORPH),
783 s3_g_start
784 .position
785 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
786 s3_g_end
787 .position
788 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
789 s3_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
790 s3_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
791 s3_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
792 s3_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
793 ],
794 wait!(1),
795 // Morph: sunset (diagonal) -> ocean (horizontal)
796 all![
797 s3.stroke_paint.to(Paint::Gradient(ocean.clone()), MORPH),
798 s3_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
799 s3_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
800 s3_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
801 s3_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
802 s3_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
803 s3_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
804 ],
805 wait!(1),
806 // Direction morph: horizontal ocean -> vertical ocean
807 all![
808 s3.stroke_paint
809 .to(Paint::Gradient(ocean_vert.clone()), MORPH),
810 s3_g_start.position.to(Vec2::new(cx, cy - 120.0), MORPH),
811 s3_g_end.position.to(Vec2::new(cx, cy + 120.0), MORPH),
812 s3_g_line.start.to(Vec2::new(0.0, -120.0), MORPH),
813 s3_g_line.end.to(Vec2::new(0.0, 120.0), MORPH),
814 ],
815 wait!(1),
816 // Direction morph: vertical ocean -> horizontal ocean
817 all![
818 s3.stroke_paint.to(Paint::Gradient(ocean.clone()), MORPH),
819 s3_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
820 s3_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
821 s3_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
822 s3_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
823 ],
824 wait!(1),
825 all![
826 s3_lbl.opacity.to(0.0, FADE_OUT),
827 s3.opacity.to(0.0, FADE_OUT),
828 s3.scale.to(Vec2::splat(0.95), FADE_OUT),
829 s3_g_start.opacity.to(0.0, FADE_OUT),
830 s3_g_end.opacity.to(0.0, FADE_OUT),
831 s3_g_line.opacity.to(0.0, FADE_OUT),
832 ],
833 // ─── 4: Line gradient stroke ───
834 all![
835 s4_lbl.opacity.to(1.0, FADE_IN),
836 s4.opacity.to(1.0, FADE_IN),
837 s4_g_start.opacity.to(1.0, FADE_IN),
838 s4_g_end.opacity.to(1.0, FADE_IN),
839 s4_g_line.opacity.to(1.0, FADE_IN),
840 ],
841 wait!(1),
842 // Morph: sunset (diagonal) -> ocean (horizontal)
843 all![
844 s4.stroke_paint.to(Paint::Gradient(ocean.clone()), MORPH),
845 s4_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
846 s4_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
847 s4_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
848 s4_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
849 s4_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
850 s4_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
851 ],
852 wait!(1),
853 // Morph: ocean (horizontal) -> sunset (diagonal)
854 all![
855 s4.stroke_paint.to(Paint::Gradient(sunset.clone()), MORPH),
856 s4_g_start
857 .position
858 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
859 s4_g_end
860 .position
861 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
862 s4_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
863 s4_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
864 s4_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
865 s4_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
866 ],
867 wait!(1),
868 all![
869 s4_lbl.opacity.to(0.0, FADE_OUT),
870 s4.opacity.to(0.0, FADE_OUT),
871 s4_g_start.opacity.to(0.0, FADE_OUT),
872 s4_g_end.opacity.to(0.0, FADE_OUT),
873 s4_g_line.opacity.to(0.0, FADE_OUT),
874 ],
875 // ─── 5: Path gradient stroke ───
876 all![
877 s5_lbl.opacity.to(1.0, FADE_IN),
878 s5.opacity.to(1.0, FADE_IN),
879 s5_g_start.opacity.to(1.0, FADE_IN),
880 s5_g_end.opacity.to(1.0, FADE_IN),
881 s5_g_line.opacity.to(1.0, FADE_IN),
882 ],
883 wait!(1),
884 // Morph: ocean (horizontal) -> sunset (diagonal)
885 all![
886 s5.stroke_paint.to(Paint::Gradient(sunset.clone()), MORPH),
887 s5_g_start
888 .position
889 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
890 s5_g_end
891 .position
892 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
893 s5_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
894 s5_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
895 s5_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
896 s5_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
897 ],
898 wait!(1),
899 // Morph: sunset (diagonal) -> ocean (horizontal)
900 all![
901 s5.stroke_paint.to(Paint::Gradient(ocean.clone()), MORPH),
902 s5_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
903 s5_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
904 s5_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
905 s5_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
906 s5_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
907 s5_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
908 ],
909 wait!(1),
910 all![
911 s5_lbl.opacity.to(0.0, FADE_OUT),
912 s5.opacity.to(0.0, FADE_OUT),
913 s5_g_start.opacity.to(0.0, FADE_OUT),
914 s5_g_end.opacity.to(0.0, FADE_OUT),
915 s5_g_line.opacity.to(0.0, FADE_OUT),
916 ],
917 // ─── 6: Grid gradient stroke ───
918 all![
919 s6_lbl.opacity.to(1.0, FADE_IN),
920 s6_grid.opacity.to(1.0, FADE_IN),
921 s6_g_start.opacity.to(1.0, FADE_IN),
922 s6_g_end.opacity.to(1.0, FADE_IN),
923 s6_g_line.opacity.to(1.0, FADE_IN),
924 ],
925 wait!(1),
926 // Morph: ocean (horizontal) -> sunset (diagonal)
927 all![
928 s6_grid
929 .stroke_paint
930 .to(Paint::Gradient(sunset.clone()), MORPH),
931 s6_g_start
932 .position
933 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
934 s6_g_end
935 .position
936 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
937 s6_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
938 s6_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
939 s6_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
940 s6_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
941 ],
942 wait!(1),
943 // Morph: sunset (diagonal) -> ocean (horizontal)
944 all![
945 s6_grid
946 .stroke_paint
947 .to(Paint::Gradient(ocean.clone()), MORPH),
948 s6_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
949 s6_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
950 s6_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
951 s6_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
952 s6_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
953 s6_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
954 ],
955 wait!(1),
956 all![
957 s6_lbl.opacity.to(0.0, FADE_OUT),
958 s6_grid.opacity.to(0.0, FADE_OUT),
959 s6_g_start.opacity.to(0.0, FADE_OUT),
960 s6_g_end.opacity.to(0.0, FADE_OUT),
961 s6_g_line.opacity.to(0.0, FADE_OUT),
962 ],
963 // ─── 7: Text — morph colors, then direction ───
964 all![
965 s7_lbl.opacity.to(1.0, FADE_IN),
966 s7.opacity.to(1.0, FADE_IN),
967 s7.scale.to(Vec2::ONE, FADE_IN),
968 s7_g_start.opacity.to(1.0, FADE_IN),
969 s7_g_end.opacity.to(1.0, FADE_IN),
970 s7_g_line.opacity.to(1.0, FADE_IN),
971 ],
972 wait!(1),
973 // Morph: ocean (horizontal) -> sunset (diagonal)
974 all![
975 s7.fill_paint.to(Paint::Gradient(sunset.clone()), MORPH),
976 s7_g_start
977 .position
978 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
979 s7_g_end
980 .position
981 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
982 s7_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
983 s7_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
984 s7_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
985 s7_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
986 ],
987 wait!(1),
988 // Morph: sunset (diagonal) -> ocean (horizontal)
989 all![
990 s7.fill_paint.to(Paint::Gradient(ocean.clone()), MORPH),
991 s7_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
992 s7_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
993 s7_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
994 s7_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
995 s7_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
996 s7_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
997 ],
998 wait!(1),
999 // Morph: horizontal ocean -> vertical ocean
1000 all![
1001 s7.fill_paint.to(Paint::Gradient(ocean_vert.clone()), MORPH),
1002 s7_g_start.position.to(Vec2::new(cx, cy - 120.0), MORPH),
1003 s7_g_end.position.to(Vec2::new(cx, cy + 120.0), MORPH),
1004 s7_g_line.start.to(Vec2::new(0.0, -120.0), MORPH),
1005 s7_g_line.end.to(Vec2::new(0.0, 120.0), MORPH),
1006 ],
1007 wait!(1),
1008 // Morph: vertical ocean -> horizontal ocean
1009 all![
1010 s7.fill_paint.to(Paint::Gradient(ocean.clone()), MORPH),
1011 s7_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
1012 s7_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
1013 s7_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
1014 s7_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
1015 ],
1016 wait!(1),
1017 all![
1018 s7_lbl.opacity.to(0.0, FADE_OUT),
1019 s7.opacity.to(0.0, FADE_OUT),
1020 s7.scale.to(Vec2::splat(0.95), FADE_OUT),
1021 s7_g_start.opacity.to(0.0, FADE_OUT),
1022 s7_g_end.opacity.to(0.0, FADE_OUT),
1023 s7_g_line.opacity.to(0.0, FADE_OUT),
1024 ],
1025 // ─── 8: Math ───
1026 all![
1027 s8_lbl.opacity.to(1.0, FADE_IN),
1028 s8.opacity.to(1.0, FADE_IN),
1029 s8.scale.to(Vec2::ONE, FADE_IN),
1030 s8_g_start.opacity.to(1.0, FADE_IN),
1031 s8_g_end.opacity.to(1.0, FADE_IN),
1032 s8_g_line.opacity.to(1.0, FADE_IN),
1033 ],
1034 wait!(1),
1035 // Morph: sunset (diagonal) -> ocean (horizontal)
1036 all![
1037 s8.fill_paint.to(Paint::Gradient(ocean.clone()), MORPH),
1038 s8_g_start.position.to(Vec2::new(cx - 120.0, cy), MORPH),
1039 s8_g_end.position.to(Vec2::new(cx + 120.0, cy), MORPH),
1040 s8_g_line.start.to(Vec2::new(-120.0, 0.0), MORPH),
1041 s8_g_line.end.to(Vec2::new(120.0, 0.0), MORPH),
1042 s8_g_start.stroke_paint.to(Paint::Solid(CYAN), MORPH),
1043 s8_g_end.stroke_paint.to(Paint::Solid(INDIGO), MORPH),
1044 ],
1045 wait!(1),
1046 // Morph: ocean (horizontal) -> sunset (diagonal)
1047 all![
1048 s8.fill_paint.to(Paint::Gradient(sunset.clone()), MORPH),
1049 s8_g_start
1050 .position
1051 .to(Vec2::new(cx - 120.0, cy - 120.0), MORPH),
1052 s8_g_end
1053 .position
1054 .to(Vec2::new(cx + 120.0, cy + 120.0), MORPH),
1055 s8_g_line.start.to(Vec2::new(-120.0, -120.0), MORPH),
1056 s8_g_line.end.to(Vec2::new(120.0, 120.0), MORPH),
1057 s8_g_start.stroke_paint.to(Paint::Solid(CORAL), MORPH),
1058 s8_g_end.stroke_paint.to(Paint::Solid(VIOLET), MORPH),
1059 ],
1060 wait!(1),
1061 all![
1062 s8_lbl.opacity.to(0.0, FADE_OUT),
1063 s8.opacity.to(0.0, FADE_OUT),
1064 s8.scale.to(Vec2::splat(0.95), FADE_OUT),
1065 s8_g_start.opacity.to(0.0, FADE_OUT),
1066 s8_g_end.opacity.to(0.0, FADE_OUT),
1067 s8_g_line.opacity.to(0.0, FADE_OUT),
1068 ],
1069 // ─── 9: Solid ↔ Gradient ───
1070 all![
1071 s9_lbl.opacity.to(1.0, FADE_IN),
1072 s9.opacity.to(1.0, FADE_IN),
1073 s9.scale.to(Vec2::ONE, FADE_IN),
1074 ],
1075 wait!(1),
1076 // Morph: solid white -> sunset (diagonal gradient) + fade in guides
1077 all![
1078 s9.fill_paint.to(Paint::Gradient(sunset.clone()), MORPH),
1079 s9_g_start.opacity.to(1.0, MORPH),
1080 s9_g_end.opacity.to(1.0, MORPH),
1081 s9_g_line.opacity.to(1.0, MORPH),
1082 ],
1083 wait!(1),
1084 // Morph: sunset -> solid white + fade out guides
1085 all![
1086 s9.fill_paint.to(Paint::Solid(Color::WHITE), MORPH),
1087 s9_g_start.opacity.to(0.0, MORPH),
1088 s9_g_end.opacity.to(0.0, MORPH),
1089 s9_g_line.opacity.to(0.0, MORPH),
1090 ],
1091 wait!(1),
1092 all![
1093 s9_lbl.opacity.to(0.0, FADE_OUT),
1094 s9.opacity.to(0.0, FADE_OUT),
1095 s9.scale.to(Vec2::splat(0.95), FADE_OUT),
1096 ],
1097 // ─── 10: Radial ↔ Linear Morph ───
1098 all![
1099 s10_lbl.opacity.to(1.0, FADE_IN),
1100 s10.opacity.to(1.0, FADE_IN),
1101 s10.scale.to(Vec2::ONE, FADE_IN),
1102 s10_g_rad_center.opacity.to(1.0, FADE_IN),
1103 s10_g_rad_radius.opacity.to(1.0, FADE_IN),
1104 ],
1105 wait!(1),
1106 // Morph Radial (glow) to Linear (sunset) + cross-fade the visual guides
1107 all![
1108 s10.fill_paint.to(Paint::Gradient(sunset.clone()), MORPH),
1109 s10_g_rad_center.opacity.to(0.0, MORPH),
1110 s10_g_rad_radius.opacity.to(0.0, MORPH),
1111 s10_g_lin_start.opacity.to(1.0, MORPH),
1112 s10_g_lin_end.opacity.to(1.0, MORPH),
1113 s10_g_lin_line.opacity.to(1.0, MORPH),
1114 ],
1115 wait!(1),
1116 // Morph back from Linear (sunset) to Radial (glow)
1117 all![
1118 s10.fill_paint.to(Paint::Gradient(glow.clone()), MORPH),
1119 s10_g_rad_center.opacity.to(1.0, MORPH),
1120 s10_g_rad_radius.opacity.to(1.0, MORPH),
1121 s10_g_lin_start.opacity.to(0.0, MORPH),
1122 s10_g_lin_end.opacity.to(0.0, MORPH),
1123 s10_g_lin_line.opacity.to(0.0, MORPH),
1124 ],
1125 wait!(1),
1126 // Fade everything out
1127 all![
1128 s10_lbl.opacity.to(0.0, FADE_OUT),
1129 s10.opacity.to(0.0, FADE_OUT),
1130 s10_g_rad_center.opacity.to(0.0, FADE_OUT),
1131 s10_g_rad_radius.opacity.to(0.0, FADE_OUT),
1132 title.opacity.to(0.0, FADE_OUT),
1133 grid.opacity.to(0.0, FADE_OUT),
1134 ],
1135 ]);
1136
1137 project.show().expect("Failed to render animation");
1138}More examples
86fn hline(y: f32) -> Line {
87 Line::default()
88 .with_start(Vec2::new(LEFT, y))
89 .with_end(Vec2::new(LEFT, y))
90 .with_stroke(Color::rgba8(255, 255, 255, 25), 1.0)
91}
92// Shorthand for show/hide
93fn show(opacity: &Signal<f32>, d: Duration) -> Box<dyn Animation> {
94 opacity.to(1.0, d).ease(easings::cubic_out).into()
95}
96fn hide(opacity: &Signal<f32>, d: Duration) -> Box<dyn Animation> {
97 opacity.to(0.0, d).ease(easings::cubic_in).into()
98}
99
100fn main() {
101 let mut project = Project::default()
102 .with_dimensions(CANVAS_W, CANVAS_H)
103 .with_fps(60)
104 .with_title("Explainer")
105 .with_background(BG)
106 .close_on_finish();
107
108 // =====================================================================
109 // S1: TITLE CARD
110 // =====================================================================
111 let s1_line = hline(100.0);
112 let s1_title = TextNode::default()
113 .with_anchor(Vec2::new(-1.0, -1.0))
114 .with_position(Vec2::new(LEFT, 120.0))
115 .with_text("motion-canvas-rs")
116 .with_font_size(52.0)
117 .with_fill(ACCENT)
118 .with_font(FONT)
119 .with_opacity(0.0);
120 let s1_sub = h2("A GPU-Accelerated Vector Animation Engine", 185.0);
121 let s1_built = body(
122 "Built on Vello + Typst — Inspired by Motion Canvas",
123 220.0,
124 );
125 let s1_desc = body(
126 "This animation will teach you how the library works,",
127 280.0,
128 );
129 let s1_desc2 = body(
130 "from struct definitions to the GPU rendering pipeline.",
131 305.0,
132 );
133
134 let s1_logo = SvgNode::default()
135 .with_position(Vec2::new(1142.0, 543.0))
136 .with_path("examples/images/motion-canvas-rs.svg")
137 .with_scale(0.3)
138 .with_size(Vec2::new(768.0, 768.0))
139 .with_opacity(0.0);
140
141 for n in [&s1_title, &s1_sub, &s1_built, &s1_desc, &s1_desc2] {
142 project.scene.add(n);
143 }
144 project.scene.add(&s1_line);
145 project.scene.add(&s1_logo);
146
147 // =====================================================================
148 // S2: THE 5 STEPS
149 // =====================================================================
150 let s2_h = title("Every program follows 5 steps", 50.0);
151 let steps = [
152 "1. Create a Project — your canvas settings",
153 "2. Create Nodes — shapes, text, images",
154 "3. Add Nodes to Scene — what gets drawn",
155 "4. Animate the Timeline — how things move",
156 "5. Show or Export — live window or video",
157 ];
158 let s2_texts: Vec<TextNode> = steps
159 .iter()
160 .enumerate()
161 .map(|(i, s)| body(s, 120.0 + i as f32 * 35.0))
162 .collect();
163
164 project.scene.add(&s2_h);
165 for t in &s2_texts {
166 project.scene.add(t);
167 }
168
169 // =====================================================================
170 // S3: WHAT IS A STRUCT? + The Project struct
171 // =====================================================================
172 let s3_h = title("What is a 'struct'?", 50.0);
173 let s3_explain = h2(
174 "A struct is a container that groups related data together.",
175 95.0,
176 );
177 let s3_analogy = body(
178 "Think of it like a class in Python/JS, but it only holds data.",
179 130.0,
180 );
181
182 let s3_code = code_block(
183 "pub struct Project {
184 pub width: u32, // Canvas width in pixels
185 pub height: u32, // Canvas height in pixels
186 pub fps: u32, // Frames per second
187 pub title: String, // Window title
188 pub scene: BaseScene, // Holds nodes + timelines
189 pub background_color: Color,
190 pub close_on_finish: bool,
191}",
192 175.0,
193 );
194
195 let s3_note = note(
196 "^ This is the actual Project struct from the library.",
197 420.0,
198 );
199 let s3_note2 = body(
200 "'pub' means public — anyone can read/write these fields.",
201 455.0,
202 );
203 let s3_note3 = body(
204 "u32 = unsigned 32-bit integer, String = text, bool = true/false",
205 485.0,
206 );
207
208 for n in [
209 &s3_h,
210 &s3_explain,
211 &s3_analogy,
212 &s3_note,
213 &s3_note2,
214 &s3_note3,
215 ] {
216 project.scene.add(n);
217 }
218 project.scene.add(&s3_code);
219
220 // =====================================================================
221 // S4: WHAT IS impl? + Builder pattern
222 // =====================================================================
223 let s4_h = title("What is 'impl'?", 50.0);
224 let s4_explain = h2("impl adds methods (functions) to a struct.", 95.0);
225 let s4_analogy = body(
226 "Like adding methods to a class. Separated from the data.",
227 130.0,
228 );
229
230 let s4_code = code_block(
231 "impl Project {
232 pub fn with_fps(mut self, fps: u32) -> Self {
233 self.fps = fps; // set the value
234 self // return yourself (builder pattern)
235 }
236 pub fn with_title(mut self, title: &str) -> Self {
237 self.title = title.to_string();
238 self
239 }
240}",
241 175.0,
242 );
243
244 let s4_usage = body("Usage — chain calls to configure:", 430.0);
245 let s4_usage_code = code_block(
246 "let project = Project::default()
247 .with_fps(60)
248 .with_title(\"My Animation\")
249 .with_dimensions(800, 600)
250 .close_on_finish();",
251 460.0,
252 );
253
254 let s4_note = note(
255 "Each .with_*() returns 'self', so you can chain them.",
256 590.0,
257 );
258
259 for n in [&s4_h, &s4_explain, &s4_analogy, &s4_usage, &s4_note] {
260 project.scene.add(n);
261 }
262 project.scene.add(&s4_code);
263 project.scene.add(&s4_usage_code);
264
265 // =====================================================================
266 // S5: WHAT IS A TRAIT? + The Node trait
267 // =====================================================================
268 let s5_h = title("What is a 'trait'?", 50.0);
269 let s5_explain = h2(
270 "A trait is a contract — like an interface in Java/TypeScript.",
271 95.0,
272 );
273 let s5_analogy = body(
274 "Any type that implements a trait promises to provide those methods.",
275 130.0,
276 );
277
278 let s5_code = code_block(
279 "pub trait Node: Send + Sync + 'static {
280 fn render(&self, scene: &mut Scene,
281 parent_transform: Affine,
282 parent_opacity: f32);
283 fn update(&mut self, dt: Duration);
284 fn state_hash(&self) -> u64;
285 fn clone_node(&self) -> Box<dyn Node>;
286}",
287 175.0,
288 );
289
290 let s5_r = note(
291 "render() — draw yourself using current signal values",
292 410.0,
293 );
294 let s5_u = note(
295 "update(dt) — called every frame (for per-frame logic)",
296 435.0,
297 );
298 let s5_s = note(
299 "state_hash() — returns a number that changes when you change",
300 460.0,
301 );
302 let s5_c = note("clone_node() — make a deep copy of yourself", 485.0);
303 let s5_every = body(
304 "Circle, Rect, Line, TextNode, Polygon all implement Node.",
305 530.0,
306 );
307
308 for n in [
309 &s5_h,
310 &s5_explain,
311 &s5_analogy,
312 &s5_r,
313 &s5_u,
314 &s5_s,
315 &s5_c,
316 &s5_every,
317 ] {
318 project.scene.add(n);
319 }
320 project.scene.add(&s5_code);
321
322 // =====================================================================
323 // S6: NODE GALLERY (visual demo)
324 // =====================================================================
325 let s6_h = title("The Built-in Nodes", 50.0);
326 let s6_sub = body(
327 "Each node uses the builder pattern and stores properties as Signals.",
328 90.0,
329 );
330
331 let demo_c = Circle::default()
332 .with_position(Vec2::new(120.0, 230.0))
333 .with_radius(40.0)
334 .with_fill(RED)
335 .with_opacity(0.0);
336 let demo_r = Rect::default()
337 .with_position(Vec2::new(293.0, 230.0))
338 .with_size(Vec2::new(80.0, 80.0))
339 .with_fill(ACCENT)
340 .with_radius(8.0)
341 .with_opacity(0.0);
342 let demo_l = Line::default()
343 .with_start(Vec2::new(420.0, 200.0))
344 .with_end(Vec2::new(510.0, 270.0))
345 .with_stroke(WHITE, 3.0)
346 .with_opacity(0.0);
347 let demo_p = Polygon::regular(5, 40.0)
348 .with_position(Vec2::new(606.0, 230.0))
349 .with_fill(YELLOW)
350 .with_opacity(0.0);
351 let demo_t = TextNode::default()
352 .with_position(Vec2::new(765.0, 230.0))
353 .with_text("Abc")
354 .with_font_size(36.0)
355 .with_fill(GREEN)
356 .with_font(FONT)
357 .with_opacity(0.0);
358
359 let lc = dim("Circle", 95.0, 285.0);
360 let lr = dim("Rect", 280.0, 285.0);
361 let ll = dim("Line", 445.0, 285.0);
362 let lp = dim("Polygon", 580.0, 285.0);
363 let lt = dim("TextNode", 735.0, 285.0);
364
365 let s6_box_h = h2("Why Box<dyn Node>?", 340.0);
366 let s6_box1 = body("The scene stores different node types in one list:", 375.0);
367 let s6_box_code = code_block(
368 "pub struct BaseScene {
369 pub nodes: Vec<Box<dyn Node>>, // a list of \"any Node\"
370}
371// 'Box' = heap-allocated, 'dyn Node' = any type implementing Node
372// Like List<INode> in Java or Array<Node> in TypeScript
373project.scene.add(circle); // wrap + add",
374 405.0,
375 );
376
377 for n in [&s6_h, &s6_sub, &lc, &lr, &ll, &lp, <, &s6_box_h, &s6_box1] {
378 project.scene.add(n);
379 }
380 project.scene.add(&demo_c);
381 project.scene.add(&demo_r);
382 project.scene.add(&demo_l);
383 project.scene.add(&demo_p);
384 project.scene.add(&demo_t);
385 project.scene.add(&s6_box_code);
386
387 // =====================================================================
388 // S7: SIGNALS — The Reactive Core
389 // =====================================================================
390 let s7_h = title("Signals — The Reactive Core", 50.0);
391 let s7_sub = h2("Every animatable property is a Signal<T>.", 95.0);
392
393 let s7_code = code_block(
394 "pub struct Signal<T> {
395 pub data: Arc<Mutex<SignalData<T>>>,
396}
397pub struct SignalData<T> {
398 pub value: T, // the actual value (f32, Vec2, Color...)
399}",
400 140.0,
401 );
402
403 let s7_arc = note("Arc = shared pointer. Multiple owners, same data.", 310.0);
404 let s7_mutex = note(
405 "Mutex = lock. Only one thread reads/writes at a time.",
406 335.0,
407 );
408 let s7_why = body(
409 "Why? A node and its animation both need the same property:",
410 380.0,
411 );
412
413 let s7_diagram_code = code_block(
414 "let circle = Circle::default().with_radius(50.0);
415// circle.radius is a Signal<f32>
416
417circle.radius.to(100.0, Duration::from_secs(1));
418// Creates a SignalTween with a CLONE of circle.radius
419// Both point to the SAME underlying value (via Arc)
420
421// The animation WRITES new values each frame
422// The node READS them when rendering",
423 420.0,
424 );
425
426 // Live demo circle
427 let sig_demo = Circle::default()
428 .with_position(Vec2::new(1000.0, 400.0))
429 .with_radius(50.0)
430 .with_fill(RED)
431 .with_stroke(Color::rgba8(255, 255, 255, 50), 2.0)
432 .with_opacity(0.0);
433 let sig_lbl = dim("Live Signal demo", 900.0, 150.0);
434
435 for n in [&s7_h, &s7_sub, &s7_arc, &s7_mutex, &s7_why, &sig_lbl] {
436 project.scene.add(n);
437 }
438 project.scene.add(&s7_code);
439 project.scene.add(&s7_diagram_code);
440 project.scene.add(&sig_demo);
441
442 // =====================================================================
443 // S8: SIGNAL TWEEN — How animations work per-frame
444 // =====================================================================
445 let s8_h = title("SignalTween — The Animation Engine", 50.0);
446 let s8_sub = body(
447 ".to() creates a SignalTween that interpolates over time:",
448 90.0,
449 );
450
451 let s8_code = code_block(
452 "pub struct SignalTween<T> {
453 data: Arc<Mutex<SignalData<T>>>, // shared ref to signal
454 start_value: Option<T>, // captured on FIRST update (lazy!)
455 target_value: Option<T>, // where we're going
456 duration: Duration, // how long
457 elapsed: Duration, // how much time passed
458 easing: fn(f32) -> f32, // curve function
459}",
460 125.0,
461 );
462
463 let s8_how = h2("Each frame update:", 340.0);
464 let s8_steps = [
465 "1. elapsed += dt",
466 "2. t_linear = elapsed / duration (0.0 to 1.0)",
467 "3. t_eased = easing(t_linear) (curved)",
468 "4. value = lerp(start, target, t) (interpolate)",
469 "5. Write value into Signal (node sees it)",
470 "6. If elapsed >= duration: finished! (return leftover dt)",
471 ];
472 let s8_step_texts: Vec<TextNode> = s8_steps
473 .iter()
474 .enumerate()
475 .map(|(i, s)| body(s, 370.0 + i as f32 * 28.0))
476 .collect();
477
478 let s8_lazy = note(
479 "start_value is captured lazily — so chained tweens read the",
480 570.0,
481 );
482 let s8_lazy2 = note(
483 "correct value at their actual start time, not creation time.",
484 590.0,
485 );
486
487 // Progress bar
488 let prog_bg = Rect::default()
489 .with_position(Vec2::new(895.0, 150.0))
490 .with_size(Vec2::new(400.0, 16.0))
491 .with_fill(Color::rgba8(255, 255, 255, 15))
492 .with_radius(8.0)
493 .with_opacity(0.0);
494 let prog_fill = Rect::default()
495 .with_position(Vec2::new(695.0, 154.0))
496 .with_anchor(Vec2::new(-1.0, 0.5))
497 .with_size(Vec2::new(0.0, 16.0))
498 .with_fill(ACCENT)
499 .with_radius(8.0)
500 .with_opacity(0.0);
501 let plbl0 = dim("t=0", 700.0, 172.0);
502 let plbl1 = dim("t=1", 1070.0, 172.0);
503 let tween_ball = Circle::default()
504 .with_position(Vec2::new(900.0, 430.0))
505 .with_radius(30.0)
506 .with_fill(RED)
507 .with_opacity(0.0);
508 let tween_lbl = dim("radius animating: 30 -> 80", 760.0, 220.0);
509
510 for n in [
511 &s8_h, &s8_sub, &s8_how, &s8_lazy, &s8_lazy2, &plbl0, &plbl1, &tween_lbl,
512 ] {
513 project.scene.add(n);
514 }
515 project.scene.add(&s8_code);
516 for t in &s8_step_texts {
517 project.scene.add(t);
518 }
519 project.scene.add(&prog_bg);
520 project.scene.add(&prog_fill);
521 project.scene.add(&tween_ball);
522
523 // =====================================================================
524 // S9: TWEENABLE + EASINGS
525 // =====================================================================
526 let s9_h = title("Tweenable — What Can Be Animated", 50.0);
527 let s9_code = code_block(
528 "pub trait Tweenable: Clone + Send + Sync {
529 fn interpolate(a: &Self, b: &Self, t: f32) -> Self;
530 fn state_hash(&self) -> u64;
531}
532// Implemented for: f32, Vec2, Color, String, Affine, Vec<Vec2>
533// f32: lerp(a, b, t) = a + (b-a)*t
534// Vec2: lerp x and y independently
535// Color: lerp R,G,B,A channels independently
536// String: snap — returns 'a' until t>=1, then 'b'",
537 95.0,
538 );
539
540 let s9_easing_h = h2("Easing functions curve the linear t:", 310.0);
541 let s9_easing_desc = body("Same distance, same duration — different feel.", 340.0);
542
543 let enames = [
544 "linear",
545 "cubic_in_out",
546 "elastic_out",
547 "bounce_out",
548 "back_out",
549 ];
550 let ecolors = [WHITE, ACCENT, RED, YELLOW, GREEN];
551 let mut eballs: Vec<Circle> = Vec::new();
552 let mut elabels: Vec<TextNode> = Vec::new();
553 for (i, name) in enames.iter().enumerate() {
554 let y = 390.0 + i as f32 * 55.0;
555 let b = Circle::default()
556 .with_position(Vec2::new(250.0, y))
557 .with_radius(12.0)
558 .with_fill(ecolors[i])
559 .with_opacity(0.0);
560 let l = dim(name, LEFT, y - 5.0);
561 project.scene.add(&b);
562 project.scene.add(&l);
563 eballs.push(b);
564 elabels.push(l);
565 }
566 project.scene.add(&s9_h);
567 project.scene.add(&s9_code);
568 project.scene.add(&s9_easing_h);
569 project.scene.add(&s9_easing_desc);
570
571 // =====================================================================
572 // S10: FLOW CONTROLS
573 // =====================================================================
574 let s10_h = title("Flow Controls — Composing Animations", 50.0);
575 let s10_sub = body(
576 "Individual tweens are simple. Power comes from composing them.",
577 90.0,
578 );
579
580 // chain
581 let s10_chain_h = h2("chain![ ] — one after another", 140.0);
582 let chain_d: Vec<Circle> = (0..3)
583 .map(|i| {
584 Circle::default()
585 .with_position(Vec2::new(LEFT + 30.0 + i as f32 * 60.0, 200.0))
586 .with_radius(18.0)
587 .with_fill([RED, ACCENT, YELLOW][i])
588 .with_opacity(0.0)
589 })
590 .collect();
591
592 // all
593 let s10_all_h = h2("all![ ] — all at the same time", 270.0);
594 let all_d: Vec<Circle> = (0..3)
595 .map(|i| {
596 Circle::default()
597 .with_position(Vec2::new(LEFT + 30.0 + i as f32 * 60.0, 330.0))
598 .with_radius(18.0)
599 .with_fill([RED, ACCENT, YELLOW][i])
600 .with_opacity(0.0)
601 })
602 .collect();
603
604 // sequence
605 let s10_seq_h = h2("sequence![ ] — staggered starts", 400.0);
606 let seq_d: Vec<Circle> = (0..3)
607 .map(|i| {
608 Circle::default()
609 .with_position(Vec2::new(LEFT + 30.0 + i as f32 * 60.0, 460.0))
610 .with_radius(18.0)
611 .with_fill([RED, ACCENT, YELLOW][i])
612 .with_opacity(0.0)
613 })
614 .collect();
615
616 let s10_code = code_block(
617 "chain![ a, b, c ] // a then b then c
618all![ a, b, c ] // a + b + c together
619sequence![ 200ms, a, b, c ] // staggered
620delay![ 500ms, a ] // wait then play
621wait(1s) // pause
622any![ a, b ] // race: first wins
623loop_anim![ a, 3 ] // repeat 3 times",
624 510.0,
625 );
626
627 project.scene.add(&s10_h);
628 project.scene.add(&s10_sub);
629 project.scene.add(&s10_chain_h);
630 project.scene.add(&s10_all_h);
631 project.scene.add(&s10_seq_h);
632 project.scene.add(&s10_code);
633 for d in &chain_d {
634 project.scene.add(d);
635 }
636 for d in &all_d {
637 project.scene.add(d);
638 }
639 for d in &seq_d {
640 project.scene.add(d);
641 }
642
643 // =====================================================================
644 // S11: TIMELINE + RENDERING
645 // =====================================================================
646 let s11_h = title("The Timeline — Animation Queue", 50.0);
647 let s11_code = code_block(
648 "pub struct Timeline {
649 pub animations: Vec<Box<dyn Animation>>,
650}
651impl Timeline {
652 fn update(&mut self, mut dt: Duration) {
653 while !self.animations.is_empty() {
654 let (finished, leftover) = self.animations[0].update(dt);
655 if finished {
656 self.animations.remove(0); // pop front
657 dt = leftover; // pass leftover to next!
658 } else { break; }
659 }
660 }
661}",
662 95.0,
663 );
664
665 let s11_leftover = note(
666 "leftover propagation: if A finishes mid-frame, the remaining",
667 390.0,
668 );
669 let s11_leftover2 = note(
670 "dt is immediately given to B. No 'lost frames' at transitions.",
671 415.0,
672 );
673
674 let s11_render_h = h2("Rendering Pipeline (per frame):", 470.0);
675 let s11_steps = [
676 "1. Timeline.update(dt) => SignalTween writes to Signals",
677 "2. Node.render() => reads signals, draws shapes",
678 "3. Vello GPU => compiles scene => wgpu => pixels",
679 "4. state_hash() => seahash for hashing scene state, skip if unchanged",
680 ];
681 let s11_render_texts: Vec<TextNode> = s11_steps
682 .iter()
683 .enumerate()
684 .map(|(i, s)| body(s, 505.0 + i as f32 * 28.0))
685 .collect();
686
687 project.scene.add(&s11_h);
688 project.scene.add(&s11_code);
689 for n in [&s11_leftover, &s11_leftover2, &s11_render_h] {
690 project.scene.add(n);
691 }
692 for t in &s11_render_texts {
693 project.scene.add(t);
694 }
695
696 // =====================================================================
697 // S12: EVENT LOOP — Why an infinite loop?
698 // =====================================================================
699 let s12_h = title("Why an Infinite Loop? — The Event Loop", 50.0);
700 let s12_sub = body(
701 "GPU rendering requires a persistent event loop (winit + wgpu).",
702 90.0,
703 );
704 let s12_code = code_block(
705 "event_loop.run(|event, elwt| {
706 match event {
707 Resumed => { // GPU surface ready
708 renderer.resume(&window);
709 }
710 AboutToWait => { // run every frame
711 scene.update(dt); // advance animations
712 let hash = scene.state_hash();
713 if hash != last_hash { // dirty?
714 window.request_redraw();
715 }
716 }
717 RedrawRequested => { // GPU draw call
718 renderer.render(&scene, w, h);
719 }
720 }
721});",
722 130.0,
723 );
724 let s12_why = note(
725 "The window stays open because the GPU surface is tied to",
726 490.0,
727 );
728 let s12_why2 = note(
729 "the OS event loop. Without it, the surface is immediately dropped.",
730 510.0,
731 );
732 let s12_hash = body(
733 "state_hash() skips re-rendering unchanged frames (dirty-checking).",
734 550.0,
735 );
736
737 for n in [&s12_h, &s12_sub, &s12_why, &s12_why2, &s12_hash] {
738 project.scene.add(n);
739 }
740 project.scene.add(&s12_code);
741
742 // =====================================================================
743 // S13: HEADLESS EXPORT — GPU without a window
744 // =====================================================================
745 let s13_h = title("Headless Export: GPU -> PNG -> FFmpeg", 50.0);
746 let s13_sub = body(
747 "Same GPU rendering, but without a window — output to files.",
748 90.0,
749 );
750 let s13_code = code_block(
751 "pub struct Exporter {
752 texture: wgpu::Texture, // GPU-side image
753 output_buffer: wgpu::Buffer, // CPU-readable copy
754 renderer: Renderer, // Vello
755}
756fn export_frame(&mut self, scene) -> Vec<u8> {
757 scene.render(&mut self.scene); // 1. build shapes
758 renderer.render_to_texture(..); // 2. GPU draws
759 encoder.copy_texture_to_buffer(..); // 3. GPU -> CPU
760 output_buffer.map_async(Read, ..); // 4. read pixels
761 return pixels; // 5. raw RGBA
762}",
763 130.0,
764 );
765 let s13_cache = note(
766 "Cache: state_hash per frame. If unchanged, skip GPU entirely.",
767 420.0,
768 );
769 let s13_ffmpeg = note(
770 "FFmpeg: raw pixels piped to stdin -> libx264 -> .mkv video.",
771 445.0,
772 );
773 let s13_parallel = body(
774 "PNG saving runs on a background thread. Export is pipelined.",
775 485.0,
776 );
777
778 for n in [&s13_h, &s13_sub, &s13_cache, &s13_ffmpeg, &s13_parallel] {
779 project.scene.add(n);
780 }
781 project.scene.add(&s13_code);
782
783 // =====================================================================
784 // S14: ENGINE UTILITIES
785 // =====================================================================
786 let s14_h = title("Under the Hood: Utility Modules", 50.0);
787 let s14_sub = body(
788 "Helper systems that power the engine behind the scenes.",
789 90.0,
790 );
791 let s14_code = code_block(
792 "// src/engine/util/
793font_manager.rs // Lazy-loads system fonts via Typst
794 // Global HashMap cache with lazy_static
795
796image_manager.rs // Loads PNG + SVG (via resvg)
797 // Caches decoded images as Arc<Image>
798
799code_tokenizer.rs // Syntax highlighting via Syntect
800 // Parses code -> colored spans for CodeNode
801
802hash.rs // SeaHash: fast, deterministic fingerprints
803 // Position-aware combination
804 // Powers Rayon parallel state hashing
805
806export.rs // FFmpeg pipe: rawvideo -> libx264
807 // Audio merging with filter_complex
808 // Title sanitization for filenames",
809 130.0,
810 );
811 let s14_lazy = note(
812 "lazy_static + Mutex = global singleton, created once, cached forever.",
813 495.0,
814 );
815 let s14_arc = body(
816 "Arc<Image> lets multiple nodes share one decoded image without copies.",
817 530.0,
818 );
819 let s14_hash = note(
820 "Rayon + SeaHash = Deterministic fingerprints across runs & threads.",
821 565.0,
822 );
823
824 for n in [&s14_h, &s14_sub, &s14_lazy, &s14_arc, &s14_hash] {
825 project.scene.add(n);
826 }
827 project.scene.add(&s14_code);
828
829 // =====================================================================
830 // S15: FINALE
831 // =====================================================================
832 let fin = TextNode::default()
833 .with_anchor(Vec2::new(-1.0, -1.0))
834 .with_position(Vec2::new(LEFT, 200.0))
835 .with_text("That's how it works!")
836 .with_font_size(48.0)
837 .with_fill(ACCENT)
838 .with_font(FONT)
839 .with_opacity(0.0);
840 let fin_steps = [
841 "1. struct — data container",
842 "2. impl — methods / builder pattern",
843 "3. trait Node — interface contract",
844 "4. Box<dyn Node> — type-erased heap allocation",
845 "5. Signal<T> — Arc<Mutex> shared reactive state",
846 "6. SignalTween — per-frame lerp interpolation",
847 "7. Timeline — sequential queue + leftover dt",
848 "8. Event Loop — winit + wgpu infinite loop",
849 "9. Exporter — headless GPU -> PNG/FFmpeg",
850 "10. Utilities — font/image cache, syntax highlight",
851 ];
852 let fin_texts: Vec<TextNode> = fin_steps
853 .iter()
854 .enumerate()
855 .map(|(i, s)| body(s, 270.0 + i as f32 * 28.0))
856 .collect();
857 let fin_hint = dim("cargo run --example getting_started", LEFT, 570.0);
858
859 project.scene.add(&fin);
860 for t in &fin_texts {
861 project.scene.add(t);
862 }
863 project.scene.add(&fin_hint);
864
865 // =====================================================================
866 // ANIMATION TIMELINE
867 // =====================================================================
868 // Helper: hide_all takes a vec of opacity signals and fades them out
869 let hide_dur = ms(200);
870
871 project.scene.video_timeline.add(chain![
872 // ── S1: TITLE ──
873 s1_line
874 .end
875 .to(Vec2::new(500.0, 100.0), ms(500))
876 .ease(easings::cubic_out),
877 sequence![
878 ms(120),
879 show(&s1_title.opacity, ms(500)),
880 show(&s1_sub.opacity, ms(500)),
881 show(&s1_built.opacity, ms(500)),
882 show(&s1_logo.opacity, ms(600)),
883 show(&s1_desc.opacity, ms(500)),
884 show(&s1_desc2.opacity, ms(500)),
885 ],
886 wait(secs(5)),
887 all![
888 hide(&s1_title.opacity, hide_dur),
889 hide(&s1_sub.opacity, hide_dur),
890 hide(&s1_built.opacity, hide_dur),
891 hide(&s1_desc.opacity, hide_dur),
892 hide(&s1_desc2.opacity, hide_dur),
893 hide(&s1_logo.opacity, hide_dur),
894 s1_line.end.to(Vec2::new(LEFT, 100.0), hide_dur)
895 ],
896 wait(ms(150)),
897 // ── S2: FIVE STEPS ──
898 show(&s2_h.opacity, ms(500)),
899 wait(ms(400)),
900 sequence![
901 ms(250),
902 show(&s2_texts[0].opacity, ms(400)),
903 show(&s2_texts[1].opacity, ms(400)),
904 show(&s2_texts[2].opacity, ms(400)),
905 show(&s2_texts[3].opacity, ms(400)),
906 show(&s2_texts[4].opacity, ms(400)),
907 ],
908 wait(secs(8)),
909 all![
910 hide(&s2_h.opacity, hide_dur),
911 hide(&s2_texts[0].opacity, hide_dur),
912 hide(&s2_texts[1].opacity, hide_dur),
913 hide(&s2_texts[2].opacity, hide_dur),
914 hide(&s2_texts[3].opacity, hide_dur),
915 hide(&s2_texts[4].opacity, hide_dur)
916 ],
917 wait(ms(150)),
918 // ── S3: STRUCT ──
919 sequence![
920 ms(120),
921 show(&s3_h.opacity, ms(500)),
922 show(&s3_explain.opacity, ms(400)),
923 show(&s3_analogy.opacity, ms(400))
924 ],
925 wait(ms(500)),
926 show(&s3_code.opacity, ms(500)),
927 wait(secs(6)),
928 sequence![
929 ms(300),
930 show(&s3_note.opacity, ms(400)),
931 show(&s3_note2.opacity, ms(400)),
932 show(&s3_note3.opacity, ms(400))
933 ],
934 wait(secs(6)),
935 all![
936 hide(&s3_h.opacity, hide_dur),
937 hide(&s3_explain.opacity, hide_dur),
938 hide(&s3_analogy.opacity, hide_dur),
939 hide(&s3_code.opacity, hide_dur),
940 hide(&s3_note.opacity, hide_dur),
941 hide(&s3_note2.opacity, hide_dur),
942 hide(&s3_note3.opacity, hide_dur)
943 ],
944 wait(ms(150)),
945 // ── S4: IMPL / BUILDER ──
946 sequence![
947 ms(120),
948 show(&s4_h.opacity, ms(500)),
949 show(&s4_explain.opacity, ms(400)),
950 show(&s4_analogy.opacity, ms(400))
951 ],
952 wait(ms(500)),
953 show(&s4_code.opacity, ms(500)),
954 wait(secs(7)),
955 show(&s4_usage.opacity, ms(400)),
956 show(&s4_usage_code.opacity, ms(500)),
957 wait(secs(2)),
958 show(&s4_note.opacity, ms(400)),
959 wait(secs(5)),
960 all![
961 hide(&s4_h.opacity, hide_dur),
962 hide(&s4_explain.opacity, hide_dur),
963 hide(&s4_analogy.opacity, hide_dur),
964 hide(&s4_code.opacity, hide_dur),
965 hide(&s4_usage.opacity, hide_dur),
966 hide(&s4_usage_code.opacity, hide_dur),
967 hide(&s4_note.opacity, hide_dur)
968 ],
969 wait(ms(150)),
970 // ── S5: TRAIT / NODE ──
971 sequence![
972 ms(120),
973 show(&s5_h.opacity, ms(500)),
974 show(&s5_explain.opacity, ms(400)),
975 show(&s5_analogy.opacity, ms(400))
976 ],
977 wait(ms(500)),
978 show(&s5_code.opacity, ms(500)),
979 wait(secs(6)),
980 sequence![
981 ms(300),
982 show(&s5_r.opacity, ms(400)),
983 show(&s5_u.opacity, ms(400)),
984 show(&s5_s.opacity, ms(400)),
985 show(&s5_c.opacity, ms(400))
986 ],
987 wait(secs(2)),
988 show(&s5_every.opacity, ms(400)),
989 wait(secs(4)),
990 all![
991 hide(&s5_h.opacity, hide_dur),
992 hide(&s5_explain.opacity, hide_dur),
993 hide(&s5_analogy.opacity, hide_dur),
994 hide(&s5_code.opacity, hide_dur),
995 hide(&s5_r.opacity, hide_dur),
996 hide(&s5_u.opacity, hide_dur),
997 hide(&s5_s.opacity, hide_dur),
998 hide(&s5_c.opacity, hide_dur),
999 hide(&s5_every.opacity, hide_dur)
1000 ],
1001 wait(ms(150)),
1002 // ── S6: NODE GALLERY ──
1003 sequence![
1004 ms(120),
1005 show(&s6_h.opacity, ms(500)),
1006 show(&s6_sub.opacity, ms(400))
1007 ],
1008 wait(ms(400)),
1009 sequence![
1010 ms(200),
1011 all![show(&demo_c.opacity, ms(400)), show(&lc.opacity, ms(400))],
1012 all![show(&demo_r.opacity, ms(400)), show(&lr.opacity, ms(400))],
1013 all![show(&demo_l.opacity, ms(400)), show(&ll.opacity, ms(400))],
1014 all![show(&demo_p.opacity, ms(400)), show(&lp.opacity, ms(400))],
1015 all![show(&demo_t.opacity, ms(400)), show(<.opacity, ms(400))],
1016 ],
1017 wait(secs(2)),
1018 sequence![
1019 ms(200),
1020 show(&s6_box_h.opacity, ms(400)),
1021 show(&s6_box1.opacity, ms(400)),
1022 show(&s6_box_code.opacity, ms(500))
1023 ],
1024 wait(secs(7)),
1025 all![
1026 hide(&s6_h.opacity, hide_dur),
1027 hide(&s6_sub.opacity, hide_dur),
1028 hide(&demo_c.opacity, hide_dur),
1029 hide(&demo_r.opacity, hide_dur),
1030 hide(&demo_l.opacity, hide_dur),
1031 hide(&demo_p.opacity, hide_dur),
1032 hide(&demo_t.opacity, hide_dur),
1033 hide(&lc.opacity, hide_dur),
1034 hide(&lr.opacity, hide_dur),
1035 hide(&ll.opacity, hide_dur),
1036 hide(&lp.opacity, hide_dur),
1037 hide(<.opacity, hide_dur),
1038 hide(&s6_box_h.opacity, hide_dur),
1039 hide(&s6_box1.opacity, hide_dur),
1040 hide(&s6_box_code.opacity, hide_dur)
1041 ],
1042 wait(ms(150)),
1043 // ── S7: SIGNALS ──
1044 sequence![
1045 ms(120),
1046 show(&s7_h.opacity, ms(500)),
1047 show(&s7_sub.opacity, ms(400))
1048 ],
1049 wait(ms(400)),
1050 show(&s7_code.opacity, ms(500)),
1051 wait(secs(5)),
1052 sequence![
1053 ms(300),
1054 show(&s7_arc.opacity, ms(400)),
1055 show(&s7_mutex.opacity, ms(400))
1056 ],
1057 wait(secs(4)),
1058 show(&s7_why.opacity, ms(400)),
1059 show(&s7_diagram_code.opacity, ms(500)),
1060 wait(secs(6)),
1061 // Live demo
1062 all![
1063 show(&sig_demo.opacity, ms(300)),
1064 show(&sig_lbl.opacity, ms(300))
1065 ],
1066 chain![
1067 sig_demo.radius.to(80.0, ms(700)).ease(easings::elastic_out),
1068 sig_demo.fill_paint.to(Paint::Solid(TEAL), ms(500)),
1069 sig_demo
1070 .position
1071 .to(Vec2::new(950.0, 350.0), ms(500))
1072 .ease(easings::cubic_out),
1073 wait(ms(300)),
1074 all![
1075 sig_demo.radius.to(50.0, ms(400)),
1076 sig_demo.fill_paint.to(Paint::Solid(RED), ms(400)),
1077 sig_demo.position.to(Vec2::new(900.0, 300.0), ms(400))
1078 ],
1079 ],
1080 wait(secs(3)),
1081 all![
1082 hide(&s7_h.opacity, hide_dur),
1083 hide(&s7_sub.opacity, hide_dur),
1084 hide(&s7_code.opacity, hide_dur),
1085 hide(&s7_arc.opacity, hide_dur),
1086 hide(&s7_mutex.opacity, hide_dur),
1087 hide(&s7_why.opacity, hide_dur),
1088 hide(&s7_diagram_code.opacity, hide_dur),
1089 hide(&sig_demo.opacity, hide_dur),
1090 hide(&sig_lbl.opacity, hide_dur)
1091 ],
1092 wait(ms(150)),
1093 // ── S8: SIGNAL TWEEN ──
1094 sequence![
1095 ms(120),
1096 show(&s8_h.opacity, ms(500)),
1097 show(&s8_sub.opacity, ms(400))
1098 ],
1099 wait(ms(400)),
1100 show(&s8_code.opacity, ms(500)),
1101 wait(secs(6)),
1102 show(&s8_how.opacity, ms(300)),
1103 sequence![
1104 ms(100),
1105 show(&s8_step_texts[0].opacity, ms(250)),
1106 show(&s8_step_texts[1].opacity, ms(250)),
1107 show(&s8_step_texts[2].opacity, ms(250)),
1108 show(&s8_step_texts[3].opacity, ms(250)),
1109 show(&s8_step_texts[4].opacity, ms(250)),
1110 show(&s8_step_texts[5].opacity, ms(250)),
1111 ],
1112 wait(ms(500)),
1113 sequence![
1114 ms(100),
1115 show(&s8_lazy.opacity, ms(300)),
1116 show(&s8_lazy2.opacity, ms(300))
1117 ],
1118 wait(ms(500)),
1119 // Progress bar demo
1120 all![
1121 show(&prog_bg.opacity, ms(200)),
1122 show(&prog_fill.opacity, ms(200)),
1123 show(&plbl0.opacity, ms(200)),
1124 show(&plbl1.opacity, ms(200)),
1125 show(&tween_ball.opacity, ms(200)),
1126 show(&tween_lbl.opacity, ms(200))
1127 ],
1128 all![
1129 prog_fill
1130 .size
1131 .to(Vec2::new(400.0, 16.0), secs(2))
1132 .ease(easings::cubic_in_out),
1133 tween_ball
1134 .radius
1135 .to(80.0, secs(2))
1136 .ease(easings::cubic_in_out),
1137 ],
1138 wait(secs(3)),
1139 all![
1140 hide(&s8_h.opacity, hide_dur),
1141 hide(&s8_sub.opacity, hide_dur),
1142 hide(&s8_code.opacity, hide_dur),
1143 hide(&s8_how.opacity, hide_dur),
1144 hide(&s8_lazy.opacity, hide_dur),
1145 hide(&s8_lazy2.opacity, hide_dur),
1146 hide(&prog_bg.opacity, hide_dur),
1147 hide(&prog_fill.opacity, hide_dur),
1148 hide(&plbl0.opacity, hide_dur),
1149 hide(&plbl1.opacity, hide_dur),
1150 hide(&tween_ball.opacity, hide_dur),
1151 hide(&tween_lbl.opacity, hide_dur),
1152 hide(&s8_step_texts[0].opacity, hide_dur),
1153 hide(&s8_step_texts[1].opacity, hide_dur),
1154 hide(&s8_step_texts[2].opacity, hide_dur),
1155 hide(&s8_step_texts[3].opacity, hide_dur),
1156 hide(&s8_step_texts[4].opacity, hide_dur),
1157 hide(&s8_step_texts[5].opacity, hide_dur)
1158 ],
1159 wait(ms(150)),
1160 // ── S9: TWEENABLE + EASINGS ──
1161 show(&s9_h.opacity, ms(500)),
1162 show(&s9_code.opacity, ms(500)),
1163 wait(secs(4)),
1164 sequence![
1165 ms(60),
1166 show(&s9_easing_h.opacity, ms(300)),
1167 show(&s9_easing_desc.opacity, ms(300))
1168 ],
1169 sequence![
1170 ms(50),
1171 all![
1172 show(&eballs[0].opacity, ms(200)),
1173 show(&elabels[0].opacity, ms(200))
1174 ],
1175 all![
1176 show(&eballs[1].opacity, ms(200)),
1177 show(&elabels[1].opacity, ms(200))
1178 ],
1179 all![
1180 show(&eballs[2].opacity, ms(200)),
1181 show(&elabels[2].opacity, ms(200))
1182 ],
1183 all![
1184 show(&eballs[3].opacity, ms(200)),
1185 show(&elabels[3].opacity, ms(200))
1186 ],
1187 all![
1188 show(&eballs[4].opacity, ms(200)),
1189 show(&elabels[4].opacity, ms(200))
1190 ],
1191 ],
1192 wait(ms(300)),
1193 // Race!
1194 all![
1195 eballs[0]
1196 .position
1197 .to(Vec2::new(1050.0, 390.0), secs(2))
1198 .ease(easings::linear),
1199 eballs[1]
1200 .position
1201 .to(Vec2::new(1050.0, 445.0), secs(2))
1202 .ease(easings::cubic_in_out),
1203 eballs[2]
1204 .position
1205 .to(Vec2::new(1050.0, 500.0), secs(2))
1206 .ease(easings::elastic_out),
1207 eballs[3]
1208 .position
1209 .to(Vec2::new(1050.0, 555.0), secs(2))
1210 .ease(easings::bounce_out),
1211 eballs[4]
1212 .position
1213 .to(Vec2::new(1050.0, 610.0), secs(2))
1214 .ease(easings::back_out),
1215 ],
1216 wait(ms(500)),
1217 all![
1218 eballs[0]
1219 .position
1220 .to(Vec2::new(250.0, 390.0), secs(2))
1221 .ease(easings::linear),
1222 eballs[1]
1223 .position
1224 .to(Vec2::new(250.0, 445.0), secs(2))
1225 .ease(easings::cubic_in_out),
1226 eballs[2]
1227 .position
1228 .to(Vec2::new(250.0, 500.0), secs(2))
1229 .ease(easings::elastic_out),
1230 eballs[3]
1231 .position
1232 .to(Vec2::new(250.0, 555.0), secs(2))
1233 .ease(easings::bounce_out),
1234 eballs[4]
1235 .position
1236 .to(Vec2::new(250.0, 610.0), secs(2))
1237 .ease(easings::back_out),
1238 ],
1239 wait(ms(500)),
1240 all![
1241 hide(&s9_h.opacity, hide_dur),
1242 hide(&s9_code.opacity, hide_dur),
1243 hide(&s9_easing_h.opacity, hide_dur),
1244 hide(&s9_easing_desc.opacity, hide_dur),
1245 hide(&eballs[0].opacity, hide_dur),
1246 hide(&eballs[1].opacity, hide_dur),
1247 hide(&eballs[2].opacity, hide_dur),
1248 hide(&eballs[3].opacity, hide_dur),
1249 hide(&eballs[4].opacity, hide_dur),
1250 hide(&elabels[0].opacity, hide_dur),
1251 hide(&elabels[1].opacity, hide_dur),
1252 hide(&elabels[2].opacity, hide_dur),
1253 hide(&elabels[3].opacity, hide_dur),
1254 hide(&elabels[4].opacity, hide_dur)
1255 ],
1256 wait(ms(150)),
1257 // ── S10: FLOW CONTROLS ──
1258 sequence![
1259 ms(120),
1260 show(&s10_h.opacity, ms(500)),
1261 show(&s10_sub.opacity, ms(400))
1262 ],
1263 wait(ms(400)),
1264 // chain demo
1265 show(&s10_chain_h.opacity, ms(400)),
1266 all![
1267 show(&chain_d[0].opacity, ms(300)),
1268 show(&chain_d[1].opacity, ms(300)),
1269 show(&chain_d[2].opacity, ms(300))
1270 ],
1271 wait(ms(300)),
1272 chain![
1273 chain_d[0]
1274 .position
1275 .to(Vec2::new(700.0, 200.0), ms(500))
1276 .ease(easings::cubic_out),
1277 chain_d[1]
1278 .position
1279 .to(Vec2::new(800.0, 200.0), ms(500))
1280 .ease(easings::cubic_out),
1281 chain_d[2]
1282 .position
1283 .to(Vec2::new(900.0, 200.0), ms(500))
1284 .ease(easings::cubic_out),
1285 ],
1286 wait(secs(1)),
1287 // all demo
1288 show(&s10_all_h.opacity, ms(400)),
1289 all![
1290 show(&all_d[0].opacity, ms(300)),
1291 show(&all_d[1].opacity, ms(300)),
1292 show(&all_d[2].opacity, ms(300))
1293 ],
1294 wait(ms(300)),
1295 all![
1296 all_d[0]
1297 .position
1298 .to(Vec2::new(700.0, 330.0), ms(500))
1299 .ease(easings::cubic_out),
1300 all_d[1]
1301 .position
1302 .to(Vec2::new(800.0, 330.0), ms(500))
1303 .ease(easings::cubic_out),
1304 all_d[2]
1305 .position
1306 .to(Vec2::new(900.0, 330.0), ms(500))
1307 .ease(easings::cubic_out),
1308 ],
1309 wait(secs(1)),
1310 // sequence demo
1311 show(&s10_seq_h.opacity, ms(400)),
1312 all![
1313 show(&seq_d[0].opacity, ms(300)),
1314 show(&seq_d[1].opacity, ms(300)),
1315 show(&seq_d[2].opacity, ms(300))
1316 ],
1317 wait(ms(300)),
1318 sequence![
1319 ms(250),
1320 seq_d[0]
1321 .position
1322 .to(Vec2::new(700.0, 460.0), ms(500))
1323 .ease(easings::cubic_out),
1324 seq_d[1]
1325 .position
1326 .to(Vec2::new(800.0, 460.0), ms(500))
1327 .ease(easings::cubic_out),
1328 seq_d[2]
1329 .position
1330 .to(Vec2::new(900.0, 460.0), ms(500))
1331 .ease(easings::cubic_out),
1332 ],
1333 wait(secs(1)),
1334 show(&s10_code.opacity, ms(500)),
1335 wait(secs(8)),
1336 all![
1337 hide(&s10_h.opacity, hide_dur),
1338 hide(&s10_sub.opacity, hide_dur),
1339 hide(&s10_chain_h.opacity, hide_dur),
1340 hide(&s10_all_h.opacity, hide_dur),
1341 hide(&s10_seq_h.opacity, hide_dur),
1342 hide(&s10_code.opacity, hide_dur),
1343 hide(&chain_d[0].opacity, hide_dur),
1344 hide(&chain_d[1].opacity, hide_dur),
1345 hide(&chain_d[2].opacity, hide_dur),
1346 hide(&all_d[0].opacity, hide_dur),
1347 hide(&all_d[1].opacity, hide_dur),
1348 hide(&all_d[2].opacity, hide_dur),
1349 hide(&seq_d[0].opacity, hide_dur),
1350 hide(&seq_d[1].opacity, hide_dur),
1351 hide(&seq_d[2].opacity, hide_dur)
1352 ],
1353 wait(ms(150)),
1354 // ── S11: TIMELINE + RENDERING ──
1355 show(&s11_h.opacity, ms(500)),
1356 wait(ms(400)),
1357 show(&s11_code.opacity, ms(500)),
1358 wait(secs(7)),
1359 sequence![
1360 ms(200),
1361 show(&s11_leftover.opacity, ms(400)),
1362 show(&s11_leftover2.opacity, ms(400))
1363 ],
1364 wait(secs(4)),
1365 show(&s11_render_h.opacity, ms(400)),
1366 sequence![
1367 ms(200),
1368 show(&s11_render_texts[0].opacity, ms(350)),
1369 show(&s11_render_texts[1].opacity, ms(350)),
1370 show(&s11_render_texts[2].opacity, ms(350)),
1371 show(&s11_render_texts[3].opacity, ms(350)),
1372 ],
1373 wait(secs(7)),
1374 all![
1375 hide(&s11_h.opacity, hide_dur),
1376 hide(&s11_code.opacity, hide_dur),
1377 hide(&s11_leftover.opacity, hide_dur),
1378 hide(&s11_leftover2.opacity, hide_dur),
1379 hide(&s11_render_h.opacity, hide_dur),
1380 hide(&s11_render_texts[0].opacity, hide_dur),
1381 hide(&s11_render_texts[1].opacity, hide_dur),
1382 hide(&s11_render_texts[2].opacity, hide_dur),
1383 hide(&s11_render_texts[3].opacity, hide_dur)
1384 ],
1385 wait(ms(300)),
1386 // ── S12: EVENT LOOP ──
1387 sequence![
1388 ms(120),
1389 show(&s12_h.opacity, ms(500)),
1390 show(&s12_sub.opacity, ms(400))
1391 ],
1392 wait(ms(500)),
1393 show(&s12_code.opacity, ms(500)),
1394 wait(secs(8)),
1395 sequence![
1396 ms(200),
1397 show(&s12_why.opacity, ms(400)),
1398 show(&s12_why2.opacity, ms(400)),
1399 show(&s12_hash.opacity, ms(400))
1400 ],
1401 wait(secs(5)),
1402 all![
1403 hide(&s12_h.opacity, hide_dur),
1404 hide(&s12_sub.opacity, hide_dur),
1405 hide(&s12_code.opacity, hide_dur),
1406 hide(&s12_why.opacity, hide_dur),
1407 hide(&s12_why2.opacity, hide_dur),
1408 hide(&s12_hash.opacity, hide_dur)
1409 ],
1410 wait(ms(150)),
1411 // ── S13: HEADLESS EXPORT ──
1412 sequence![
1413 ms(120),
1414 show(&s13_h.opacity, ms(500)),
1415 show(&s13_sub.opacity, ms(400))
1416 ],
1417 wait(ms(500)),
1418 show(&s13_code.opacity, ms(500)),
1419 wait(secs(8)),
1420 sequence![
1421 ms(200),
1422 show(&s13_cache.opacity, ms(400)),
1423 show(&s13_ffmpeg.opacity, ms(400)),
1424 show(&s13_parallel.opacity, ms(400))
1425 ],
1426 wait(secs(5)),
1427 all![
1428 hide(&s13_h.opacity, hide_dur),
1429 hide(&s13_sub.opacity, hide_dur),
1430 hide(&s13_code.opacity, hide_dur),
1431 hide(&s13_cache.opacity, hide_dur),
1432 hide(&s13_ffmpeg.opacity, hide_dur),
1433 hide(&s13_parallel.opacity, hide_dur)
1434 ],
1435 wait(ms(150)),
1436 // ── S14: UTILITIES ──
1437 sequence![
1438 ms(120),
1439 show(&s14_h.opacity, ms(500)),
1440 show(&s14_sub.opacity, ms(400))
1441 ],
1442 wait(ms(500)),
1443 show(&s14_code.opacity, ms(500)),
1444 wait(secs(8)),
1445 sequence![
1446 ms(200),
1447 show(&s14_lazy.opacity, ms(400)),
1448 show(&s14_arc.opacity, ms(400))
1449 ],
1450 wait(secs(5)),
1451 all![
1452 hide(&s14_h.opacity, hide_dur),
1453 hide(&s14_sub.opacity, hide_dur),
1454 hide(&s14_code.opacity, hide_dur),
1455 hide(&s14_lazy.opacity, hide_dur),
1456 hide(&s14_arc.opacity, hide_dur)
1457 ],
1458 wait(ms(300)),
1459 // ── S15: FINALE ──
1460 show(&fin.opacity, ms(700)),
1461 wait(ms(500)),
1462 sequence![
1463 ms(150),
1464 show(&fin_texts[0].opacity, ms(350)),
1465 show(&fin_texts[1].opacity, ms(350)),
1466 show(&fin_texts[2].opacity, ms(350)),
1467 show(&fin_texts[3].opacity, ms(350)),
1468 show(&fin_texts[4].opacity, ms(350)),
1469 show(&fin_texts[5].opacity, ms(350)),
1470 show(&fin_texts[6].opacity, ms(350)),
1471 show(&fin_texts[7].opacity, ms(350)),
1472 show(&fin_texts[8].opacity, ms(350)),
1473 show(&fin_texts[9].opacity, ms(350)),
1474 ],
1475 wait(secs(2)),
1476 show(&fin_hint.opacity, ms(400)),
1477 wait(secs(6)),
1478 ]);
1479
1480 #[cfg(feature = "audio")]
1481 project
1482 .scene
1483 .audio_timeline
1484 .add(play!(AudioNode::new("background.mp3").with_volume(0.3)));
1485
1486 project.show().expect("Failed to render");
1487}8fn 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}4fn main() {
5 let mut project = Project::default()
6 .with_title("Group Animation")
7 .close_on_finish();
8
9 // Create some child nodes
10 let rect = Rect::default()
11 .with_position(Vec2::new(0.0, 0.0))
12 .with_size(Vec2::new(100.0, 100.0))
13 .with_fill(Color::rgba8(100, 100, 255, 255))
14 .with_radius(10.0);
15
16 let circle1 = Circle::default()
17 .with_position(Vec2::new(-40.0, -40.0))
18 .with_radius(20.0)
19 .with_fill(Color::rgba8(255, 100, 100, 255));
20
21 let circle2 = Circle::default()
22 .with_position(Vec2::new(40.0, 40.0))
23 .with_radius(20.0)
24 .with_fill(Color::rgba8(100, 255, 100, 255));
25
26 // Create a GroupNode holding them
27 let group = GroupNode::default()
28 .with_nodes(vec![
29 Box::new(rect.clone()),
30 Box::new(circle1.clone()),
31 Box::new(circle2.clone()),
32 ])
33 .with_position(Vec2::new(400.0, 300.0));
34
35 // We must add the group to the scene's nodes to render it
36 project.scene.add(&group);
37
38 // Define animations and add them to the timeline
39 project.scene.video_timeline.add(chain![
40 // 1. Move the whole group
41 group
42 .position
43 .to(Vec2::new(200.0, 150.0), Duration::from_secs(2)),
44 // 2. Rotate the group
45 group
46 .rotation
47 .to(std::f32::consts::PI, Duration::from_secs(2)),
48 // 3. Combined transform (move + scale)
49 all![
50 group
51 .position
52 .to(Vec2::new(400.0, 450.0), Duration::from_secs(2)),
53 group.scale.to(Vec2::splat(2.0), Duration::from_secs(2)),
54 group.opacity.to(0.3, Duration::from_secs(2)),
55 ],
56 // 4. Reset (at the center)
57 all![
58 group
59 .position
60 .to(Vec2::new(400.0, 300.0), Duration::from_secs(1)),
61 group.scale.to(Vec2::ONE, Duration::from_secs(1)),
62 group.rotation.to(0.0, Duration::from_secs(1)),
63 group.opacity.to(1.0, Duration::from_secs(1)),
64 ],
65 ]);
66
67 project.show().expect("Failed to render");
68}4fn main() {
5 let mut project = Project::default()
6 .with_title("Signals")
7 .with_dimensions(800, 600)
8 .close_on_finish();
9
10 // 1. Create independent signals for our "Coordinate System"
11 // These are NOT tied to any node yet.
12 let x_var = Signal::new(0.0f32);
13 let y_var = Signal::new(0.0f32);
14
15 // 2. Create the Axis lines (Fixed)
16 let x_axis = Line::default()
17 .with_start(Vec2::new(-200.0, 0.0))
18 .with_end(Vec2::new(200.0, 0.0))
19 .with_stroke(Color::rgba8(255, 255, 255, 100), 2.0);
20
21 let y_axis = Line::default()
22 .with_start(Vec2::new(0.0, -200.0))
23 .with_end(Vec2::new(0.0, 200.0))
24 .with_stroke(Color::rgba8(255, 255, 255, 100), 2.0);
25
26 // Group the axes at the center
27 let axes = GroupNode::default()
28 .with_nodes(vec![Box::new(x_axis), Box::new(y_axis)])
29 .with_position(Vec2::new(400.0, 300.0));
30
31 // 3. Create a representation of the point (P)
32 let point = Circle::default()
33 .with_radius(8.0)
34 .with_fill(Color::rgb8(0xe1, 0x32, 0x38)); // Red
35
36 // 4. Create text labels that show the values
37 let x_label = TextNode::default()
38 .with_text("X: 0")
39 .with_font_size(24.0)
40 .with_fill(Color::WHITE);
41
42 let y_label = TextNode::default()
43 .with_text("Y: 0")
44 .with_font_size(24.0)
45 .with_fill(Color::WHITE);
46
47 // 5. Use .bind() to derive node properties from our variables
48 // This makes node properties react to x_var and y_var.
49
50 // Circle position follows both
51 let y_clone = y_var.clone();
52 let circle_pos_link = point.position.bind(x_var.clone(), move |x| {
53 Vec2::new(x + 400.0, y_clone.get() + 300.0)
54 });
55
56 // X Label follows X but stays at bottom
57 let x_label_pos_link = x_label
58 .position
59 .bind(x_var.clone(), |x| Vec2::new(x + 400.0, 550.0));
60
61 // Y Label stays at left but follows Y
62 let y_label_pos_link = y_label
63 .position
64 .bind(y_var.clone(), |y| Vec2::new(75.0, y + 300.0));
65
66 // Dynamic text update for labels
67 let x_text_link = x_label.text.bind(x_var.clone(), |x| format!("X: {:.1}", x));
68 let y_text_link = y_label.text.bind(y_var.clone(), |y| format!("Y: {:.1}", y));
69
70 // Add everything to the scene
71 project.scene.add(axes);
72 project.scene.add(point);
73 project.scene.add(x_label);
74 project.scene.add(y_label);
75
76 // Add the links as "invisible" nodes that just perform the sync
77 project.scene.add(circle_pos_link);
78 project.scene.add(x_label_pos_link);
79 project.scene.add(y_label_pos_link);
80 project.scene.add(x_text_link);
81 project.scene.add(y_text_link);
82
83 // 6. Animate our independent variables!
84 project.scene.video_timeline.add(chain![
85 // Move X
86 x_var
87 .to(200.0, Duration::from_secs(1))
88 .ease(easings::cubic_out),
89 // Wait
90 wait(Duration::from_millis(500)),
91 // Move Y
92 y_var
93 .to(-150.0, Duration::from_secs(1))
94 .ease(easings::back_out),
95 // Move both together
96 all![
97 x_var
98 .to(-250.0, Duration::from_secs(2))
99 .ease(easings::expo_in_out),
100 y_var
101 .to(100.0, Duration::from_secs(2))
102 .ease(easings::sine_in_out),
103 ],
104 // Reset
105 all![
106 x_var.to(0.0, Duration::from_secs(1)),
107 y_var.to(0.0, Duration::from_secs(1)),
108 ],
109 // Final wait to see the result
110 wait(Duration::from_secs(1)),
111 ]);
112
113 project.show().expect("Failed to render");
114}8fn main() {
9 let mut project = Project::default()
10 .with_title("Blur Demo")
11 .with_background(Color::rgb8(0x08, 0x08, 0x10))
12 .close_on_finish();
13
14 // Dot Grid Backdrop
15 for x in 1..8 {
16 for y in 1..6 {
17 project.scene.add(
18 Circle::default()
19 .with_position(Vec2::new(x as f32 * 100.0, y as f32 * 100.0))
20 .with_radius(1.5)
21 .with_fill(Color::rgb8(0x1a, 0x1a, 0x2e)),
22 );
23 }
24 }
25
26 // Title
27 let title = TextNode::default()
28 .with_position(Vec2::new(400.0, 60.0))
29 .with_text("Universal Blur")
30 .with_font_size(42.0)
31 .with_fill(Color::rgb8(0xe2, 0xe8, 0xf0))
32 .with_blur(0.0);
33 project.scene.add(&title);
34
35 // Shape
36 let orb = Circle::default()
37 .with_position(Vec2::new(180.0, 220.0))
38 .with_radius(55.0)
39 .with_fill(Color::rgb8(0xa7, 0x6b, 0xf1))
40 .with_opacity(0.9)
41 .with_blur(20.0);
42 project.scene.add(&orb);
43
44 // SVG
45 let logo = SvgNode::default()
46 .with_position(Vec2::new(620.0, 220.0))
47 .with_path("./examples/images/motion-canvas-rs.svg")
48 .with_size(Vec2::new(100.0, 100.0))
49 .with_blur(0.0);
50 project.scene.add(&logo);
51
52 // Math
53 let eq = MathNode::default()
54 .with_position(Vec2::new(200.0, 400.0))
55 .with_equation(r#"E = m c^2"#)
56 .with_font_size(38.0)
57 .with_fill(Color::rgb8(0xfb, 0xd3, 0x8d))
58 .with_blur(0.0);
59 project.scene.add(&eq);
60
61 // Code
62 let snippet = CodeNode::default()
63 .with_position(Vec2::new(400.0, 400.0))
64 .with_language("rs")
65 .with_code("let blur = 20.0;\ncircle.with_blur(blur);")
66 .with_font_size(18.0)
67 .with_blur(0.0);
68 project.scene.add(&snippet);
69
70 // Group
71 let card = GroupNode::new(vec![
72 Box::new(
73 Rect::default()
74 .with_size(Vec2::new(200.0, 90.0))
75 .with_radius(12.0)
76 .with_fill(Color::rgba8(0x38, 0xa1, 0xdb, 0xbb)),
77 ),
78 Box::new(
79 TextNode::default()
80 .with_text("Group Blur")
81 .with_font_size(22.0)
82 .with_fill(Color::WHITE),
83 ),
84 ])
85 .with_position(Vec2::new(400.0, 300.0))
86 .with_blur(0.0);
87 project.scene.add(&card);
88
89 // Subtitle
90 let sub = TextNode::default()
91 .with_position(Vec2::new(400.0, 550.0))
92 .with_text("Shape · SVG · Math · Code · Group")
93 .with_font_size(16.0)
94 .with_fill(Color::rgb8(0x64, 0x6e, 0x7a))
95 .with_blur(0.0);
96 project.scene.add(&sub);
97
98 // Animation: staggered blur waves
99 // Phase 1 — blur everything except the orb (which sharpens)
100 // Phase 2 — restore, then blur the card and equation
101 // Phase 3 — return to initial state
102 project.scene.video_timeline.add(loop_anim!(
103 chain![
104 wait(BEAT),
105 // Phase 1: orb sharpens, everything else softens
106 all![
107 orb.blur.to(0.0, FAST),
108 logo.blur.to(18.0, FAST),
109 eq.blur.to(14.0, FAST),
110 snippet.blur.to(14.0, FAST),
111 card.blur.to(12.0, FAST),
112 title.blur.to(6.0, FAST),
113 sub.blur.to(4.0, FAST),
114 ],
115 wait(BEAT),
116 // Phase 2: card + equation sharp, rest blurred
117 all![
118 orb.blur.to(24.0, SLOW),
119 logo.blur.to(0.0, SLOW),
120 eq.blur.to(0.0, SLOW),
121 snippet.blur.to(18.0, SLOW),
122 card.blur.to(0.0, SLOW),
123 title.blur.to(0.0, SLOW),
124 sub.blur.to(0.0, SLOW),
125 ],
126 wait(BEAT),
127 // Phase 3: return to opening state
128 all![
129 orb.blur.to(20.0, FAST),
130 logo.blur.to(0.0, FAST),
131 eq.blur.to(0.0, FAST),
132 snippet.blur.to(0.0, FAST),
133 card.blur.to(0.0, FAST),
134 title.blur.to(0.0, FAST),
135 sub.blur.to(0.0, FAST),
136 ],
137 ],
138 None,
139 ));
140
141 project.show().expect("Failed to show window");
142}Sourcepub fn rgb(r: f64, g: f64, b: f64) -> Color
pub fn rgb(r: f64, g: f64, b: f64) -> Color
Create a color from three floating point values, each in the range 0.0 to 1.0.
The interpretation is the same as rgb8, and no greater precision is (currently) assumed.
Sourcepub fn rgba(r: f64, g: f64, b: f64, a: f64) -> Color
pub fn rgba(r: f64, g: f64, b: f64, a: f64) -> Color
Create a color from four floating point values, each in the range 0.0 to 1.0.
The interpretation is the same as rgba32, and no greater precision is (currently) assumed.
Sourcepub fn hlc(h: f64, l: f64, c: f64) -> Color
pub fn hlc(h: f64, l: f64, c: f64) -> Color
Create a color from a CIEL*a*b* polar (also known as CIE HCL) specification.
The h parameter is an angle in degrees, with 0 roughly magenta, 90
roughly yellow, 180 roughly cyan, and 270 roughly blue. The l
parameter is perceptual luminance, with 0 black and 100 white.
The c parameter is a chrominance concentration, with 0 grayscale
and a nominal maximum of 127 (in the future, higher values might
be useful, for high gamut contexts).
Currently this is just converted into sRGB, but in the future as we support high-gamut colorspaces, it can be used to specify more colors or existing colors with a higher accuracy.
Currently out-of-gamut values are clipped to the nearest sRGB color, which is perhaps not ideal (the clipping might change the hue). See https://github.com/d3/d3-color/issues/33 for discussion.
Sourcepub fn hlca(h: f64, l: f64, c: f64, alpha: f64) -> Color
pub fn hlca(h: f64, l: f64, c: f64, alpha: f64) -> Color
Create a color from a CIEL*a*b* polar specification and alpha.
The a value represents alpha in the range 0.0 to 1.0.
Sourcepub fn parse(s: &str) -> Option<Color>
pub fn parse(s: &str) -> Option<Color>
Parses a color from a string.
Currently accepts CSS style hexadecimal colors of the forms #RGB, #RGBA, #RRGGBB, #RRGGBBAA or the name of an SVG color such as “aliceblue”.
Sourcepub fn with_alpha_factor(self, alpha: f32) -> Color
👎Deprecated since 0.2.0: This method has been renamed to multiply_alpha.
pub fn with_alpha_factor(self, alpha: f32) -> Color
This method has been renamed to multiply_alpha.
Returns the color with the alpha component multiplied by the specified factor.
Sourcepub fn multiply_alpha(self, alpha: f32) -> Color
pub fn multiply_alpha(self, alpha: f32) -> Color
Returns the color with the alpha component multiplied by alpha.
The behaviour of this transformation is undefined if alpha is negative.
If the resulting alpha would overflow, this currently saturates (to opaque).
Sourcepub fn to_premul_u32(self) -> u32
pub fn to_premul_u32(self) -> u32
Returns the color as a packed premultiplied value.
Source§impl Color
Named SVG colors.
impl Color
Named SVG colors.
Sourcepub const ALICE_BLUE: Color
pub const ALICE_BLUE: Color
Alice blue (240, 248, 255, 255)
Sourcepub const ANTIQUE_WHITE: Color
pub const ANTIQUE_WHITE: Color
Antique white (250, 235, 215, 255)
Sourcepub const AQUAMARINE: Color
pub const AQUAMARINE: Color
Aquamarine (127, 255, 212, 255)
Sourcepub const BLANCHED_ALMOND: Color
pub const BLANCHED_ALMOND: Color
Blanched almond (255, 235, 205, 255)
Sourcepub const BLUE_VIOLET: Color
pub const BLUE_VIOLET: Color
Blue violet (138, 43, 226, 255)
Sourcepub const CADET_BLUE: Color
pub const CADET_BLUE: Color
Cadet blue (95, 158, 160, 255)
Sourcepub const CHARTREUSE: Color
pub const CHARTREUSE: Color
Chartreuse (127, 255, 0, 255)
Sourcepub const CORNFLOWER_BLUE: Color
pub const CORNFLOWER_BLUE: Color
Cornflower blue (100, 149, 237, 255)
Sourcepub const DARK_GOLDENROD: Color
pub const DARK_GOLDENROD: Color
Dark goldenrod (184, 134, 11, 255)
Sourcepub const DARK_GREEN: Color
pub const DARK_GREEN: Color
Dark green (0, 100, 0, 255)
Sourcepub const DARK_KHAKI: Color
pub const DARK_KHAKI: Color
Dark khaki (189, 183, 107, 255)
Sourcepub const DARK_MAGENTA: Color
pub const DARK_MAGENTA: Color
Dark magenta (139, 0, 139, 255)
Sourcepub const DARK_OLIVE_GREEN: Color
pub const DARK_OLIVE_GREEN: Color
Dark olive green (85, 107, 47, 255)
Sourcepub const DARK_ORANGE: Color
pub const DARK_ORANGE: Color
Dark orange (255, 140, 0, 255)
Sourcepub const DARK_ORCHID: Color
pub const DARK_ORCHID: Color
Dark orchid (153, 50, 204, 255)
Sourcepub const DARK_SALMON: Color
pub const DARK_SALMON: Color
Dark salmon (233, 150, 122, 255)
Sourcepub const DARK_SEA_GREEN: Color
pub const DARK_SEA_GREEN: Color
Dark sea green (143, 188, 143, 255)
Sourcepub const DARK_SLATE_BLUE: Color
pub const DARK_SLATE_BLUE: Color
Dark slate blue (72, 61, 139, 255)
Sourcepub const DARK_SLATE_GRAY: Color
pub const DARK_SLATE_GRAY: Color
Dark slate gray (47, 79, 79, 255)
Sourcepub const DARK_TURQUOISE: Color
pub const DARK_TURQUOISE: Color
Dark turquoise (0, 206, 209, 255)
Sourcepub const DARK_VIOLET: Color
pub const DARK_VIOLET: Color
Dark violet (148, 0, 211, 255)
Sourcepub const DEEP_SKY_BLUE: Color
pub const DEEP_SKY_BLUE: Color
Deep sky blue (0, 191, 255, 255)
Sourcepub const DODGER_BLUE: Color
pub const DODGER_BLUE: Color
Dodger blue (30, 144, 255, 255)
Sourcepub const FLORAL_WHITE: Color
pub const FLORAL_WHITE: Color
Floral white (255, 250, 240, 255)
Sourcepub const FOREST_GREEN: Color
pub const FOREST_GREEN: Color
Forest green (34, 139, 34, 255)
Sourcepub const GHOST_WHITE: Color
pub const GHOST_WHITE: Color
Ghost white (248, 248, 255, 255)
Sourcepub const GREEN_YELLOW: Color
pub const GREEN_YELLOW: Color
Green yellow (173, 255, 47, 255)
Sourcepub const INDIAN_RED: Color
pub const INDIAN_RED: Color
Indian red (205, 92, 92, 255)
Sourcepub const LAVENDER_BLUSH: Color
pub const LAVENDER_BLUSH: Color
Lavender blush (255, 240, 245, 255)
Sourcepub const LAWN_GREEN: Color
pub const LAWN_GREEN: Color
Lawn green (124, 252, 0, 255)
Sourcepub const LEMON_CHIFFON: Color
pub const LEMON_CHIFFON: Color
Lemon chiffon (255, 250, 205, 255)
Sourcepub const LIGHT_BLUE: Color
pub const LIGHT_BLUE: Color
Light blue (173, 216, 230, 255)
Sourcepub const LIGHT_CORAL: Color
pub const LIGHT_CORAL: Color
Light coral (240, 128, 128, 255)
Sourcepub const LIGHT_CYAN: Color
pub const LIGHT_CYAN: Color
Light cyan (224, 255, 255, 255)
Sourcepub const LIGHT_GOLDENROD_YELLOW: Color
pub const LIGHT_GOLDENROD_YELLOW: Color
Light goldenrod yellow (250, 250, 210, 255)
Sourcepub const LIGHT_GRAY: Color
pub const LIGHT_GRAY: Color
Light gray (211, 211, 211, 255)
Sourcepub const LIGHT_GREEN: Color
pub const LIGHT_GREEN: Color
Light green (144, 238, 144, 255)
Sourcepub const LIGHT_PINK: Color
pub const LIGHT_PINK: Color
Light pink (255, 182, 193, 255)
Sourcepub const LIGHT_SALMON: Color
pub const LIGHT_SALMON: Color
Light salmon (255, 160, 122, 255)
Sourcepub const LIGHT_SEA_GREEN: Color
pub const LIGHT_SEA_GREEN: Color
Light sea green (32, 178, 170, 255)
Sourcepub const LIGHT_SKY_BLUE: Color
pub const LIGHT_SKY_BLUE: Color
Light sky blue (135, 206, 250, 255)
Sourcepub const LIGHT_SLATE_GRAY: Color
pub const LIGHT_SLATE_GRAY: Color
Light slate gray (119, 136, 153, 255)
Sourcepub const LIGHT_STEEL_BLUE: Color
pub const LIGHT_STEEL_BLUE: Color
Light steel blue (176, 196, 222, 255)
Sourcepub const LIGHT_YELLOW: Color
pub const LIGHT_YELLOW: Color
Light yellow (255, 255, 224, 255)
Sourcepub const LIME_GREEN: Color
pub const LIME_GREEN: Color
Lime green (50, 205, 50, 255)
Sourcepub const MEDIUM_AQUAMARINE: Color
pub const MEDIUM_AQUAMARINE: Color
Medium aquamarine (102, 205, 170, 255)
Sourcepub const MEDIUM_BLUE: Color
pub const MEDIUM_BLUE: Color
Medium blue (0, 0, 205, 255)
Sourcepub const MEDIUM_ORCHID: Color
pub const MEDIUM_ORCHID: Color
Medium orchid (186, 85, 211, 255)
Sourcepub const MEDIUM_PURPLE: Color
pub const MEDIUM_PURPLE: Color
Medium purple (147, 112, 219, 255)
Sourcepub const MEDIUM_SEA_GREEN: Color
pub const MEDIUM_SEA_GREEN: Color
Medium sea green (60, 179, 113, 255)
Sourcepub const MEDIUM_SLATE_BLUE: Color
pub const MEDIUM_SLATE_BLUE: Color
Medium slate blue (123, 104, 238, 255)
Sourcepub const MEDIUM_SPRING_GREEN: Color
pub const MEDIUM_SPRING_GREEN: Color
Medium spring green (0, 250, 154, 255)
Sourcepub const MEDIUM_TURQUOISE: Color
pub const MEDIUM_TURQUOISE: Color
Medium turquoise (72, 209, 204, 255)
Sourcepub const MEDIUM_VIOLET_RED: Color
pub const MEDIUM_VIOLET_RED: Color
Medium violet red (199, 21, 133, 255)
Sourcepub const MIDNIGHT_BLUE: Color
pub const MIDNIGHT_BLUE: Color
Midnight blue (25, 25, 112, 255)
Sourcepub const MINT_CREAM: Color
pub const MINT_CREAM: Color
Mint cream (245, 255, 250, 255)
Sourcepub const MISTY_ROSE: Color
pub const MISTY_ROSE: Color
Misty rose (255, 228, 225, 255)
Sourcepub const NAVAJO_WHITE: Color
pub const NAVAJO_WHITE: Color
Navajo white (255, 222, 173, 255)
Sourcepub const OLIVE_DRAB: Color
pub const OLIVE_DRAB: Color
Olive drab (107, 142, 35, 255)
Sourcepub const ORANGE_RED: Color
pub const ORANGE_RED: Color
Orange red (255, 69, 0, 255)
Sourcepub const PALE_GOLDENROD: Color
pub const PALE_GOLDENROD: Color
Pale goldenrod (238, 232, 170, 255)
Sourcepub const PALE_GREEN: Color
pub const PALE_GREEN: Color
Pale green (152, 251, 152, 255)
Sourcepub const PALE_TURQUOISE: Color
pub const PALE_TURQUOISE: Color
Pale turquoise (175, 238, 238, 255)
Sourcepub const PALE_VIOLET_RED: Color
pub const PALE_VIOLET_RED: Color
Pale violet red (219, 112, 147, 255)
Sourcepub const PAPAYA_WHIP: Color
pub const PAPAYA_WHIP: Color
Papaya whip (255, 239, 213, 255)
Sourcepub const PEACH_PUFF: Color
pub const PEACH_PUFF: Color
Peach puff (255, 218, 185, 255)
Sourcepub const POWDER_BLUE: Color
pub const POWDER_BLUE: Color
Powder blue (176, 224, 230, 255)
Sourcepub const REBECCA_PURPLE: Color
pub const REBECCA_PURPLE: Color
Rebecca purple (102, 51, 153, 255)
Sourcepub const ROSY_BROWN: Color
pub const ROSY_BROWN: Color
Rosy brown (188, 143, 143, 255)
Sourcepub const ROYAL_BLUE: Color
pub const ROYAL_BLUE: Color
Royal blue (65, 105, 225, 255)
Sourcepub const SADDLE_BROWN: Color
pub const SADDLE_BROWN: Color
Saddle brown (139, 69, 19, 255)
Sourcepub const SANDY_BROWN: Color
pub const SANDY_BROWN: Color
Sandy brown (244, 164, 96, 255)
Sourcepub const SLATE_BLUE: Color
pub const SLATE_BLUE: Color
Slate blue (106, 90, 205, 255)
Sourcepub const SLATE_GRAY: Color
pub const SLATE_GRAY: Color
Slate gray (112, 128, 144, 255)
Sourcepub const SPRING_GREEN: Color
pub const SPRING_GREEN: Color
Spring green (0, 255, 127, 255)
Sourcepub const STEEL_BLUE: Color
pub const STEEL_BLUE: Color
Steel blue (70, 130, 180, 255)
Sourcepub const TRANSPARENT: Color
pub const TRANSPARENT: Color
Transparent (0, 0, 0, 0)
Sourcepub const WHITE_SMOKE: Color
pub const WHITE_SMOKE: Color
White smoke (245, 245, 245, 255)
Sourcepub const YELLOW_GREEN: Color
pub const YELLOW_GREEN: Color
Yellow green (154, 205, 50, 255)
Trait Implementations§
Source§impl Ord for Color
impl Ord for Color
1.21.0 (const: unstable) · Source§fn max(self, other: Self) -> Selfwhere
Self: Sized,
fn max(self, other: Self) -> Selfwhere
Self: Sized,
Source§impl PartialOrd for Color
impl PartialOrd for Color
Source§impl Tweenable for Color
impl Tweenable for Color
Source§fn interpolate(a: &Self, b: &Self, t: f32) -> Self
fn interpolate(a: &Self, b: &Self, t: f32) -> Self
a and b by factor t (0.0 to 1.0).Source§fn state_hash(&self) -> u64
fn state_hash(&self) -> u64
impl Copy for Color
impl Eq for Color
impl StructuralPartialEq for Color
Auto Trait Implementations§
impl Freeze for Color
impl RefUnwindSafe for Color
impl Send for Color
impl Sync for Color
impl Unpin for Color
impl UnsafeUnpin for Color
impl UnwindSafe for Color
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>,
Source§fn adapt_into(self) -> D
fn adapt_into(self) -> D
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
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
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
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
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
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>,
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>
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Comparable<K> for Q
impl<Q, K> Comparable<K> for Q
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
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>
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>
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)
&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)
&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<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
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>
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
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
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
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
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
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
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
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>
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>
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
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>,
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)
Source§impl<T> Pointable for T
impl<T> Pointable for T
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>,
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
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>,
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
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.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
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>
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>>
OutOfBounds error is returned which contains
the unclamped color. Read more