1use glam::Vec2;
18use wisp::application::Application;
19use wisp::math::Rect;
20use wisp::{Color, Fill, Graphics, Node, Stage};
21
22use crate::story::Story;
23
24const FOCAL: Vec2 = Vec2::new(0.40, -0.30);
28const AMOUNT: f32 = 2.4;
30
31pub fn story() -> Story {
32 Story {
33 id: "editor-zoom-pushin",
34 category: "Editor",
35 title: "Zoom push-in — the rostrum move",
36 milestone: "ED.16",
37 writeup: include_str!("writeups/editor_zoom.md"),
38 build,
39 tick: Some(tick),
40 }
41}
42
43fn build(_app: &Application, stage: &mut Stage) {
44 let mut g = Graphics::new();
45
46 g.fill(Fill::LinearGradient {
50 start: Vec2::new(0.0, 1.0),
51 end: Vec2::new(0.0, -1.0),
52 color_a: Color::rgb_u8(70, 82, 104),
53 color_b: Color::rgb_u8(40, 47, 62),
54 });
55 g.draw_rect(Rect::new(-1.0, -1.0, 2.0, 2.0));
56
57 g.fill(Fill::Solid(Color::rgb_u8(248, 249, 251)));
59 g.draw_rounded_rect(Rect::new(-0.82, -0.72, 1.64, 1.44), 0.07);
60 g.fill(Fill::Solid(Color::rgb_u8(228, 232, 238)));
62 g.draw_rect(Rect::new(-0.80, 0.52, 1.60, 0.18));
63 for (x, c) in [
65 (-0.720, Color::rgb_u8(237, 106, 94)),
66 (-0.645, Color::rgb_u8(244, 191, 79)),
67 (-0.570, Color::rgb_u8(98, 197, 84)),
68 ] {
69 g.fill(Fill::Solid(c));
70 g.draw_ellipse(Vec2::new(x, 0.61), Vec2::splat(0.022));
71 }
72
73 g.fill(Fill::Solid(Color::rgb_u8(78, 88, 104)));
75 g.draw_rounded_rect(Rect::new(-0.66, 0.34, 0.62, 0.09), 0.03);
76 g.fill(Fill::Solid(Color::rgb_u8(199, 205, 214)));
77 for (y, w) in [(0.20, 1.18), (0.07, 0.96), (-0.06, 1.10)] {
78 g.draw_rounded_rect(Rect::new(-0.66, y, w, 0.055), 0.025);
79 }
80
81 let bw = 0.34;
84 let bh = 0.15;
85 g.fill(Fill::Solid(Color::rgb_u8(56, 120, 242)));
86 g.draw_rounded_rect(
87 Rect::new(FOCAL.x - bw / 2.0, FOCAL.y - bh / 2.0, bw, bh),
88 0.045,
89 );
90 g.fill(Fill::Solid(Color::rgb_u8(232, 240, 255)));
92 g.draw_rounded_rect(
93 Rect::new(FOCAL.x - 0.10, FOCAL.y - 0.022, 0.20, 0.044),
94 0.022,
95 );
96
97 let _ = stage.add_child(stage.root(), g);
98}
99
100fn zoom_scale(t: f32) -> f32 {
104 let ease = |p: f32| {
106 if p < 0.5 {
107 4.0 * p * p * p
108 } else {
109 1.0 - (-2.0 * p + 2.0).powi(3) / 2.0
110 }
111 };
112 if t < 0.9 {
113 1.0 + (AMOUNT - 1.0) * ease((t / 0.9).clamp(0.0, 1.0))
114 } else if t < 2.1 {
115 AMOUNT
116 } else {
117 AMOUNT - (AMOUNT - 1.0) * ease(((t - 2.1) / 0.9).clamp(0.0, 1.0))
118 }
119}
120
121fn tick(stage: &mut Stage, t: f32) {
122 let z = zoom_scale(t);
123 let child = stage
124 .get(stage.root())
125 .and_then(|n| n.container().children().next());
126 if let Some(id) = child
127 && let Some(Node::Graphics(g)) = stage.get_mut(id)
128 {
129 g.container.transform.scale = Vec2::splat(z);
133 g.container.transform.position = FOCAL * (1.0 - z);
134 }
135}