pub struct DrawContext<'a> { /* private fields */ }Expand description
Drawing context passed to the canvas on_draw callback.
Provides primitives for drawing pixels, lines, rectangles, and circles.
Implementations§
Source§impl<'a> DrawContext<'a>
impl<'a> DrawContext<'a>
Sourcepub fn new(buffer: &'a mut PixelBuffer) -> Self
pub fn new(buffer: &'a mut PixelBuffer) -> Self
Create a new draw context wrapping a pixel buffer.
Sourcepub fn dimensions(&self) -> (u16, u16)
pub fn dimensions(&self) -> (u16, u16)
Get canvas dimensions (width, height) in pixels.
Sourcepub fn clear(&mut self, color: Color)
pub fn clear(&mut self, color: Color)
Clear the canvas to a solid color.
Examples found in repository?
examples/29_canvas.rs (lines 68-72)
31 fn render(&self, cx: Scope) -> View {
32 let show_help = state!(cx, || false);
33
34 // F1 toggles help
35 cx.use_command(
36 KeyBinding::key(KeyCode::F(1)),
37 with!(show_help => move || show_help.update(|v| *v = !*v)),
38 );
39
40 // Animated value for the bar chart using stream macro
41 let frame_stream = stream!(cx, || {
42 (0u32..).inspect(|&i| {
43 if i > 0 {
44 std::thread::sleep(std::time::Duration::from_millis(100));
45 }
46 })
47 });
48
49 let current_frame = frame_stream.get();
50
51 View::vstack()
52 .spacing(1)
53 .child(
54 View::styled_text("Canvas Examples (Kitty Graphics)")
55 .bold()
56 .build(),
57 )
58 .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
59 .child(View::text(""))
60 // Basic shapes demo
61 .child(View::text("Basic Shapes:"))
62 .child(
63 View::canvas()
64 .width(200)
65 .height(80)
66 .on_draw(|ctx| {
67 // Clear to dark background
68 ctx.clear(Color::Rgb {
69 r: 30,
70 g: 30,
71 b: 40,
72 });
73
74 // Draw some lines
75 ctx.line(10, 10, 190, 10, Color::Red);
76 ctx.line(10, 10, 10, 70, Color::Green);
77 ctx.line(10, 70, 190, 70, Color::Blue);
78 ctx.line(190, 10, 190, 70, Color::Yellow);
79
80 // Draw diagonal lines
81 ctx.line(10, 10, 190, 70, Color::Cyan);
82 ctx.line(10, 70, 190, 10, Color::Magenta);
83
84 // Draw filled rectangles
85 ctx.fill_rect(30, 25, 30, 20, Color::Red);
86 ctx.fill_rect(80, 25, 30, 20, Color::Green);
87 ctx.fill_rect(130, 25, 30, 20, Color::Blue);
88
89 // Draw stroked rectangles
90 ctx.stroke_rect(30, 50, 30, 15, Color::Yellow);
91 ctx.stroke_rect(80, 50, 30, 15, Color::Cyan);
92 ctx.stroke_rect(130, 50, 30, 15, Color::Magenta);
93 })
94 .build(),
95 )
96 .child(View::text(""))
97 // Circles demo
98 .child(View::text("Circles:"))
99 .child(
100 View::canvas()
101 .width(200)
102 .height(60)
103 .on_draw(|ctx| {
104 ctx.clear(Color::Rgb {
105 r: 20,
106 g: 25,
107 b: 35,
108 });
109
110 // Filled circles
111 ctx.fill_circle(30, 30, 20, Color::Red);
112 ctx.fill_circle(80, 30, 15, Color::Green);
113 ctx.fill_circle(120, 30, 10, Color::Blue);
114
115 // Stroked circles
116 ctx.circle(160, 30, 20, Color::Yellow);
117 ctx.circle(160, 30, 15, Color::Cyan);
118 ctx.circle(160, 30, 10, Color::Magenta);
119 })
120 .build(),
121 )
122 .child(View::text(""))
123 // Animated bar chart
124 .child(View::text("Animated Bar Chart:"))
125 .child(
126 View::canvas()
127 .width(200)
128 .height(80)
129 .on_draw({
130 move |ctx| {
131 ctx.clear(Color::Rgb {
132 r: 25,
133 g: 25,
134 b: 30,
135 });
136
137 // Generate animated data
138 let data: Vec<f32> = (0..8)
139 .map(|i| {
140 let phase = (current_frame as f32 * 0.1) + (i as f32 * 0.5);
141 0.3 + 0.7 * ((phase.sin() + 1.0) / 2.0)
142 })
143 .collect();
144
145 let bar_width = 20u16;
146 let gap = 5u16;
147 let max_height = 60u16;
148 let start_x = 10u16;
149 let baseline = 75u16;
150
151 // Draw baseline
152 ctx.line(5, baseline as i32, 195, baseline as i32, Color::Grey);
153
154 // Draw bars
155 let colors = [
156 Color::Red,
157 Color::Green,
158 Color::Blue,
159 Color::Yellow,
160 Color::Cyan,
161 Color::Magenta,
162 Color::Rgb {
163 r: 255,
164 g: 128,
165 b: 0,
166 },
167 Color::Rgb {
168 r: 128,
169 g: 255,
170 b: 128,
171 },
172 ];
173
174 for (i, &value) in data.iter().enumerate() {
175 let x = start_x + (i as u16) * (bar_width + gap);
176 let height = (value * max_height as f32) as u16;
177 let y = baseline - height;
178 ctx.fill_rect(x, y, bar_width, height, colors[i % colors.len()]);
179 }
180 }
181 })
182 .build(),
183 )
184 .child(View::text(""))
185 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
186 .child(
187 View::modal()
188 .visible(show_help.get())
189 .title("Example 29: Canvas")
190 .on_dismiss(with!(show_help => move || show_help.set(false)))
191 .child(
192 View::vstack()
193 .child(View::styled_text("What you're seeing").bold().build())
194 .child(View::text("• Pixel graphics via Kitty protocol"))
195 .child(View::text("• Lines, rectangles, circles"))
196 .child(View::text("• Animated bar chart"))
197 .child(View::gap(1))
198 .child(View::styled_text("Key concepts").bold().build())
199 .child(View::text("• View::canvas() creates drawing area"))
200 .child(View::text("• .on_draw(|ctx| { ... }) draws pixels"))
201 .child(View::text("• ctx.line(), ctx.fill_rect(), etc."))
202 .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
203 .child(View::gap(1))
204 .child(View::styled_text("Try this").bold().build())
205 .child(View::text("• Watch the animated bar chart"))
206 .child(View::text("• Run in compatible terminal"))
207 .child(View::gap(1))
208 .child(View::styled_text("Next up").bold().build())
209 .child(View::text("→ 30_image: image display"))
210 .child(View::gap(1))
211 .child(View::styled_text("Press Escape to close").dim().build())
212 .build(),
213 )
214 .build(),
215 )
216 .build()
217 }More examples
examples/31_animated_canvas.rs (lines 58-62)
33 fn render(&self, cx: Scope) -> View {
34 let show_help = state!(cx, || false);
35
36 // F1 toggles help
37 cx.use_command(
38 KeyBinding::key(KeyCode::F(1)),
39 with!(show_help => move || show_help.update(|v| *v = !*v)),
40 );
41 View::vstack()
42 .spacing(1)
43 .child(
44 View::styled_text("Animated Canvas Demo (Kitty Graphics)")
45 .bold()
46 .build(),
47 )
48 .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
49 .child(View::text(""))
50 // Bouncing ball animation
51 .child(View::text("Bouncing Ball (60 FPS):"))
52 .child(
53 animated_canvas(cx.clone())
54 .width(200)
55 .height(60)
56 .fps(60)
57 .on_frame(|ctx, frame| {
58 ctx.clear(Color::Rgb {
59 r: 20,
60 g: 20,
61 b: 30,
62 });
63
64 // Ball physics
65 let t = frame as f32 * 0.05;
66 let x = 100.0 + 80.0 * t.cos();
67 let y = 30.0 + 20.0 * (t * 1.5).sin().abs();
68
69 // Shadow
70 ctx.fill_circle(
71 x as u16,
72 55,
73 8,
74 Color::Rgb {
75 r: 40,
76 g: 40,
77 b: 50,
78 },
79 );
80
81 // Ball with gradient effect (outer to inner)
82 ctx.fill_circle(
83 x as u16,
84 y as u16,
85 12,
86 Color::Rgb {
87 r: 200,
88 g: 50,
89 b: 50,
90 },
91 );
92 ctx.fill_circle(
93 x as u16,
94 y as u16,
95 8,
96 Color::Rgb {
97 r: 255,
98 g: 80,
99 b: 80,
100 },
101 );
102 ctx.fill_circle(
103 x as u16 - 2,
104 y as u16 - 2,
105 3,
106 Color::Rgb {
107 r: 255,
108 g: 200,
109 b: 200,
110 },
111 );
112
113 // Ground line
114 ctx.line(10, 58, 190, 58, Color::Grey);
115 })
116 .build(),
117 )
118 .child(View::text(""))
119 // Sine wave animation
120 .child(View::text("Sine Waves (30 FPS):"))
121 .child(
122 animated_canvas(cx.clone())
123 .width(200)
124 .height(50)
125 .fps(30)
126 .on_frame(|ctx, frame| {
127 ctx.clear(Color::Rgb {
128 r: 15,
129 g: 25,
130 b: 35,
131 });
132
133 let phase = frame as f32 * 0.1;
134
135 // Draw multiple sine waves
136 let colors = [
137 Color::Rgb {
138 r: 255,
139 g: 100,
140 b: 100,
141 },
142 Color::Rgb {
143 r: 100,
144 g: 255,
145 b: 100,
146 },
147 Color::Rgb {
148 r: 100,
149 g: 100,
150 b: 255,
151 },
152 ];
153
154 for (wave_idx, color) in colors.iter().enumerate() {
155 let offset = wave_idx as f32 * 0.5;
156 let amplitude = 15.0 - wave_idx as f32 * 3.0;
157
158 for x in 0..200 {
159 let t = x as f32 * 0.05 + phase + offset;
160 let y = 25.0 + amplitude * t.sin();
161 ctx.pixel(x, y as u16, *color);
162
163 // Make line thicker
164 if y as u16 > 0 {
165 ctx.pixel(x, y as u16 - 1, *color);
166 }
167 }
168 }
169
170 // Center line
171 ctx.line(0, 25, 199, 25, Color::DarkGrey);
172 })
173 .build(),
174 )
175 .child(View::text(""))
176 // Particle effect
177 .child(View::text("Particle Fountain (45 FPS):"))
178 .child(
179 animated_canvas(cx.clone())
180 .width(200)
181 .height(60)
182 .fps(45)
183 .on_frame(|ctx, frame| {
184 ctx.clear(Color::Rgb {
185 r: 10,
186 g: 10,
187 b: 20,
188 });
189
190 // Simple particle system using deterministic "random" based on frame
191 let num_particles = 30;
192 for i in 0..num_particles {
193 // Each particle has a lifecycle based on frame offset
194 let particle_frame = (frame + i * 7) % 60;
195 let life = particle_frame as f32 / 60.0;
196
197 // Starting position (center bottom)
198 let start_x = 100.0;
199 let start_y = 55.0;
200
201 // Velocity varies per particle (deterministic "random")
202 let angle = -1.57 + (i as f32 * 0.21).sin() * 0.8; // Around -90 degrees
203 let speed = 40.0 + (i as f32 * 0.37).cos() * 20.0;
204
205 // Physics: position = start + velocity * time + 0.5 * gravity * time^2
206 let vx = angle.cos() * speed;
207 let vy = angle.sin() * speed;
208 let gravity = 80.0;
209
210 let x = start_x + vx * life;
211 let y = start_y + vy * life + 0.5 * gravity * life * life;
212
213 // Fade out as particle ages
214 let alpha = (1.0 - life) * 255.0;
215
216 // Color based on age (yellow -> orange -> red)
217 let r = 255;
218 let g = ((1.0 - life * 0.7) * 200.0) as u8;
219 let b = ((1.0 - life) * 100.0) as u8;
220
221 if (0.0..200.0).contains(&x) && (0.0..60.0).contains(&y) {
222 let color = Color::Rgb { r, g, b };
223 ctx.pixel_alpha(x as u16, y as u16, color, alpha as u8);
224 // Make particles slightly larger
225 if x > 0.0 {
226 ctx.pixel_alpha(
227 x as u16 - 1,
228 y as u16,
229 color,
230 (alpha * 0.5) as u8,
231 );
232 }
233 if y > 0.0 {
234 ctx.pixel_alpha(
235 x as u16,
236 y as u16 - 1,
237 color,
238 (alpha * 0.5) as u8,
239 );
240 }
241 }
242 }
243
244 // Emitter glow
245 ctx.fill_circle(100, 55, 3, Color::Yellow);
246 })
247 .build(),
248 )
249 .child(View::text(""))
250 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
251 .child(
252 View::modal()
253 .visible(show_help.get())
254 .title("Example 31: Animated Canvas")
255 .on_dismiss(with!(show_help => move || show_help.set(false)))
256 .child(
257 View::vstack()
258 .child(View::styled_text("What you're seeing").bold().build())
259 .child(View::text("• Bouncing ball animation (60 FPS)"))
260 .child(View::text("• Sine wave visualization (30 FPS)"))
261 .child(View::text("• Particle fountain (45 FPS)"))
262 .child(View::gap(1))
263 .child(View::styled_text("Key concepts").bold().build())
264 .child(View::text("• animated_canvas(cx) helper"))
265 .child(View::text("• .fps() sets frame rate"))
266 .child(View::text("• .on_frame(|ctx, frame| { }) draws"))
267 .child(View::text("• Frame counter enables animation"))
268 .child(View::gap(1))
269 .child(View::styled_text("Try this").bold().build())
270 .child(View::text("• Watch the smooth animations"))
271 .child(View::text("• Notice different FPS rates"))
272 .child(View::gap(1))
273 .child(View::styled_text("Next up").bold().build())
274 .child(View::text("→ 32_effects: side effects"))
275 .child(View::gap(1))
276 .child(View::styled_text("Press Escape to close").dim().build())
277 .build(),
278 )
279 .build(),
280 )
281 .build()
282 }Sourcepub fn pixel(&mut self, x: u16, y: u16, color: Color)
pub fn pixel(&mut self, x: u16, y: u16, color: Color)
Set a single pixel.
Examples found in repository?
examples/31_animated_canvas.rs (line 161)
33 fn render(&self, cx: Scope) -> View {
34 let show_help = state!(cx, || false);
35
36 // F1 toggles help
37 cx.use_command(
38 KeyBinding::key(KeyCode::F(1)),
39 with!(show_help => move || show_help.update(|v| *v = !*v)),
40 );
41 View::vstack()
42 .spacing(1)
43 .child(
44 View::styled_text("Animated Canvas Demo (Kitty Graphics)")
45 .bold()
46 .build(),
47 )
48 .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
49 .child(View::text(""))
50 // Bouncing ball animation
51 .child(View::text("Bouncing Ball (60 FPS):"))
52 .child(
53 animated_canvas(cx.clone())
54 .width(200)
55 .height(60)
56 .fps(60)
57 .on_frame(|ctx, frame| {
58 ctx.clear(Color::Rgb {
59 r: 20,
60 g: 20,
61 b: 30,
62 });
63
64 // Ball physics
65 let t = frame as f32 * 0.05;
66 let x = 100.0 + 80.0 * t.cos();
67 let y = 30.0 + 20.0 * (t * 1.5).sin().abs();
68
69 // Shadow
70 ctx.fill_circle(
71 x as u16,
72 55,
73 8,
74 Color::Rgb {
75 r: 40,
76 g: 40,
77 b: 50,
78 },
79 );
80
81 // Ball with gradient effect (outer to inner)
82 ctx.fill_circle(
83 x as u16,
84 y as u16,
85 12,
86 Color::Rgb {
87 r: 200,
88 g: 50,
89 b: 50,
90 },
91 );
92 ctx.fill_circle(
93 x as u16,
94 y as u16,
95 8,
96 Color::Rgb {
97 r: 255,
98 g: 80,
99 b: 80,
100 },
101 );
102 ctx.fill_circle(
103 x as u16 - 2,
104 y as u16 - 2,
105 3,
106 Color::Rgb {
107 r: 255,
108 g: 200,
109 b: 200,
110 },
111 );
112
113 // Ground line
114 ctx.line(10, 58, 190, 58, Color::Grey);
115 })
116 .build(),
117 )
118 .child(View::text(""))
119 // Sine wave animation
120 .child(View::text("Sine Waves (30 FPS):"))
121 .child(
122 animated_canvas(cx.clone())
123 .width(200)
124 .height(50)
125 .fps(30)
126 .on_frame(|ctx, frame| {
127 ctx.clear(Color::Rgb {
128 r: 15,
129 g: 25,
130 b: 35,
131 });
132
133 let phase = frame as f32 * 0.1;
134
135 // Draw multiple sine waves
136 let colors = [
137 Color::Rgb {
138 r: 255,
139 g: 100,
140 b: 100,
141 },
142 Color::Rgb {
143 r: 100,
144 g: 255,
145 b: 100,
146 },
147 Color::Rgb {
148 r: 100,
149 g: 100,
150 b: 255,
151 },
152 ];
153
154 for (wave_idx, color) in colors.iter().enumerate() {
155 let offset = wave_idx as f32 * 0.5;
156 let amplitude = 15.0 - wave_idx as f32 * 3.0;
157
158 for x in 0..200 {
159 let t = x as f32 * 0.05 + phase + offset;
160 let y = 25.0 + amplitude * t.sin();
161 ctx.pixel(x, y as u16, *color);
162
163 // Make line thicker
164 if y as u16 > 0 {
165 ctx.pixel(x, y as u16 - 1, *color);
166 }
167 }
168 }
169
170 // Center line
171 ctx.line(0, 25, 199, 25, Color::DarkGrey);
172 })
173 .build(),
174 )
175 .child(View::text(""))
176 // Particle effect
177 .child(View::text("Particle Fountain (45 FPS):"))
178 .child(
179 animated_canvas(cx.clone())
180 .width(200)
181 .height(60)
182 .fps(45)
183 .on_frame(|ctx, frame| {
184 ctx.clear(Color::Rgb {
185 r: 10,
186 g: 10,
187 b: 20,
188 });
189
190 // Simple particle system using deterministic "random" based on frame
191 let num_particles = 30;
192 for i in 0..num_particles {
193 // Each particle has a lifecycle based on frame offset
194 let particle_frame = (frame + i * 7) % 60;
195 let life = particle_frame as f32 / 60.0;
196
197 // Starting position (center bottom)
198 let start_x = 100.0;
199 let start_y = 55.0;
200
201 // Velocity varies per particle (deterministic "random")
202 let angle = -1.57 + (i as f32 * 0.21).sin() * 0.8; // Around -90 degrees
203 let speed = 40.0 + (i as f32 * 0.37).cos() * 20.0;
204
205 // Physics: position = start + velocity * time + 0.5 * gravity * time^2
206 let vx = angle.cos() * speed;
207 let vy = angle.sin() * speed;
208 let gravity = 80.0;
209
210 let x = start_x + vx * life;
211 let y = start_y + vy * life + 0.5 * gravity * life * life;
212
213 // Fade out as particle ages
214 let alpha = (1.0 - life) * 255.0;
215
216 // Color based on age (yellow -> orange -> red)
217 let r = 255;
218 let g = ((1.0 - life * 0.7) * 200.0) as u8;
219 let b = ((1.0 - life) * 100.0) as u8;
220
221 if (0.0..200.0).contains(&x) && (0.0..60.0).contains(&y) {
222 let color = Color::Rgb { r, g, b };
223 ctx.pixel_alpha(x as u16, y as u16, color, alpha as u8);
224 // Make particles slightly larger
225 if x > 0.0 {
226 ctx.pixel_alpha(
227 x as u16 - 1,
228 y as u16,
229 color,
230 (alpha * 0.5) as u8,
231 );
232 }
233 if y > 0.0 {
234 ctx.pixel_alpha(
235 x as u16,
236 y as u16 - 1,
237 color,
238 (alpha * 0.5) as u8,
239 );
240 }
241 }
242 }
243
244 // Emitter glow
245 ctx.fill_circle(100, 55, 3, Color::Yellow);
246 })
247 .build(),
248 )
249 .child(View::text(""))
250 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
251 .child(
252 View::modal()
253 .visible(show_help.get())
254 .title("Example 31: Animated Canvas")
255 .on_dismiss(with!(show_help => move || show_help.set(false)))
256 .child(
257 View::vstack()
258 .child(View::styled_text("What you're seeing").bold().build())
259 .child(View::text("• Bouncing ball animation (60 FPS)"))
260 .child(View::text("• Sine wave visualization (30 FPS)"))
261 .child(View::text("• Particle fountain (45 FPS)"))
262 .child(View::gap(1))
263 .child(View::styled_text("Key concepts").bold().build())
264 .child(View::text("• animated_canvas(cx) helper"))
265 .child(View::text("• .fps() sets frame rate"))
266 .child(View::text("• .on_frame(|ctx, frame| { }) draws"))
267 .child(View::text("• Frame counter enables animation"))
268 .child(View::gap(1))
269 .child(View::styled_text("Try this").bold().build())
270 .child(View::text("• Watch the smooth animations"))
271 .child(View::text("• Notice different FPS rates"))
272 .child(View::gap(1))
273 .child(View::styled_text("Next up").bold().build())
274 .child(View::text("→ 32_effects: side effects"))
275 .child(View::gap(1))
276 .child(View::styled_text("Press Escape to close").dim().build())
277 .build(),
278 )
279 .build(),
280 )
281 .build()
282 }Sourcepub fn pixel_alpha(&mut self, x: u16, y: u16, color: Color, alpha: u8)
pub fn pixel_alpha(&mut self, x: u16, y: u16, color: Color, alpha: u8)
Set a pixel with alpha.
Examples found in repository?
examples/31_animated_canvas.rs (line 223)
33 fn render(&self, cx: Scope) -> View {
34 let show_help = state!(cx, || false);
35
36 // F1 toggles help
37 cx.use_command(
38 KeyBinding::key(KeyCode::F(1)),
39 with!(show_help => move || show_help.update(|v| *v = !*v)),
40 );
41 View::vstack()
42 .spacing(1)
43 .child(
44 View::styled_text("Animated Canvas Demo (Kitty Graphics)")
45 .bold()
46 .build(),
47 )
48 .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
49 .child(View::text(""))
50 // Bouncing ball animation
51 .child(View::text("Bouncing Ball (60 FPS):"))
52 .child(
53 animated_canvas(cx.clone())
54 .width(200)
55 .height(60)
56 .fps(60)
57 .on_frame(|ctx, frame| {
58 ctx.clear(Color::Rgb {
59 r: 20,
60 g: 20,
61 b: 30,
62 });
63
64 // Ball physics
65 let t = frame as f32 * 0.05;
66 let x = 100.0 + 80.0 * t.cos();
67 let y = 30.0 + 20.0 * (t * 1.5).sin().abs();
68
69 // Shadow
70 ctx.fill_circle(
71 x as u16,
72 55,
73 8,
74 Color::Rgb {
75 r: 40,
76 g: 40,
77 b: 50,
78 },
79 );
80
81 // Ball with gradient effect (outer to inner)
82 ctx.fill_circle(
83 x as u16,
84 y as u16,
85 12,
86 Color::Rgb {
87 r: 200,
88 g: 50,
89 b: 50,
90 },
91 );
92 ctx.fill_circle(
93 x as u16,
94 y as u16,
95 8,
96 Color::Rgb {
97 r: 255,
98 g: 80,
99 b: 80,
100 },
101 );
102 ctx.fill_circle(
103 x as u16 - 2,
104 y as u16 - 2,
105 3,
106 Color::Rgb {
107 r: 255,
108 g: 200,
109 b: 200,
110 },
111 );
112
113 // Ground line
114 ctx.line(10, 58, 190, 58, Color::Grey);
115 })
116 .build(),
117 )
118 .child(View::text(""))
119 // Sine wave animation
120 .child(View::text("Sine Waves (30 FPS):"))
121 .child(
122 animated_canvas(cx.clone())
123 .width(200)
124 .height(50)
125 .fps(30)
126 .on_frame(|ctx, frame| {
127 ctx.clear(Color::Rgb {
128 r: 15,
129 g: 25,
130 b: 35,
131 });
132
133 let phase = frame as f32 * 0.1;
134
135 // Draw multiple sine waves
136 let colors = [
137 Color::Rgb {
138 r: 255,
139 g: 100,
140 b: 100,
141 },
142 Color::Rgb {
143 r: 100,
144 g: 255,
145 b: 100,
146 },
147 Color::Rgb {
148 r: 100,
149 g: 100,
150 b: 255,
151 },
152 ];
153
154 for (wave_idx, color) in colors.iter().enumerate() {
155 let offset = wave_idx as f32 * 0.5;
156 let amplitude = 15.0 - wave_idx as f32 * 3.0;
157
158 for x in 0..200 {
159 let t = x as f32 * 0.05 + phase + offset;
160 let y = 25.0 + amplitude * t.sin();
161 ctx.pixel(x, y as u16, *color);
162
163 // Make line thicker
164 if y as u16 > 0 {
165 ctx.pixel(x, y as u16 - 1, *color);
166 }
167 }
168 }
169
170 // Center line
171 ctx.line(0, 25, 199, 25, Color::DarkGrey);
172 })
173 .build(),
174 )
175 .child(View::text(""))
176 // Particle effect
177 .child(View::text("Particle Fountain (45 FPS):"))
178 .child(
179 animated_canvas(cx.clone())
180 .width(200)
181 .height(60)
182 .fps(45)
183 .on_frame(|ctx, frame| {
184 ctx.clear(Color::Rgb {
185 r: 10,
186 g: 10,
187 b: 20,
188 });
189
190 // Simple particle system using deterministic "random" based on frame
191 let num_particles = 30;
192 for i in 0..num_particles {
193 // Each particle has a lifecycle based on frame offset
194 let particle_frame = (frame + i * 7) % 60;
195 let life = particle_frame as f32 / 60.0;
196
197 // Starting position (center bottom)
198 let start_x = 100.0;
199 let start_y = 55.0;
200
201 // Velocity varies per particle (deterministic "random")
202 let angle = -1.57 + (i as f32 * 0.21).sin() * 0.8; // Around -90 degrees
203 let speed = 40.0 + (i as f32 * 0.37).cos() * 20.0;
204
205 // Physics: position = start + velocity * time + 0.5 * gravity * time^2
206 let vx = angle.cos() * speed;
207 let vy = angle.sin() * speed;
208 let gravity = 80.0;
209
210 let x = start_x + vx * life;
211 let y = start_y + vy * life + 0.5 * gravity * life * life;
212
213 // Fade out as particle ages
214 let alpha = (1.0 - life) * 255.0;
215
216 // Color based on age (yellow -> orange -> red)
217 let r = 255;
218 let g = ((1.0 - life * 0.7) * 200.0) as u8;
219 let b = ((1.0 - life) * 100.0) as u8;
220
221 if (0.0..200.0).contains(&x) && (0.0..60.0).contains(&y) {
222 let color = Color::Rgb { r, g, b };
223 ctx.pixel_alpha(x as u16, y as u16, color, alpha as u8);
224 // Make particles slightly larger
225 if x > 0.0 {
226 ctx.pixel_alpha(
227 x as u16 - 1,
228 y as u16,
229 color,
230 (alpha * 0.5) as u8,
231 );
232 }
233 if y > 0.0 {
234 ctx.pixel_alpha(
235 x as u16,
236 y as u16 - 1,
237 color,
238 (alpha * 0.5) as u8,
239 );
240 }
241 }
242 }
243
244 // Emitter glow
245 ctx.fill_circle(100, 55, 3, Color::Yellow);
246 })
247 .build(),
248 )
249 .child(View::text(""))
250 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
251 .child(
252 View::modal()
253 .visible(show_help.get())
254 .title("Example 31: Animated Canvas")
255 .on_dismiss(with!(show_help => move || show_help.set(false)))
256 .child(
257 View::vstack()
258 .child(View::styled_text("What you're seeing").bold().build())
259 .child(View::text("• Bouncing ball animation (60 FPS)"))
260 .child(View::text("• Sine wave visualization (30 FPS)"))
261 .child(View::text("• Particle fountain (45 FPS)"))
262 .child(View::gap(1))
263 .child(View::styled_text("Key concepts").bold().build())
264 .child(View::text("• animated_canvas(cx) helper"))
265 .child(View::text("• .fps() sets frame rate"))
266 .child(View::text("• .on_frame(|ctx, frame| { }) draws"))
267 .child(View::text("• Frame counter enables animation"))
268 .child(View::gap(1))
269 .child(View::styled_text("Try this").bold().build())
270 .child(View::text("• Watch the smooth animations"))
271 .child(View::text("• Notice different FPS rates"))
272 .child(View::gap(1))
273 .child(View::styled_text("Next up").bold().build())
274 .child(View::text("→ 32_effects: side effects"))
275 .child(View::gap(1))
276 .child(View::styled_text("Press Escape to close").dim().build())
277 .build(),
278 )
279 .build(),
280 )
281 .build()
282 }Sourcepub fn line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: Color)
pub fn line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: Color)
Draw a line using Bresenham’s algorithm.
Examples found in repository?
examples/29_canvas.rs (line 75)
31 fn render(&self, cx: Scope) -> View {
32 let show_help = state!(cx, || false);
33
34 // F1 toggles help
35 cx.use_command(
36 KeyBinding::key(KeyCode::F(1)),
37 with!(show_help => move || show_help.update(|v| *v = !*v)),
38 );
39
40 // Animated value for the bar chart using stream macro
41 let frame_stream = stream!(cx, || {
42 (0u32..).inspect(|&i| {
43 if i > 0 {
44 std::thread::sleep(std::time::Duration::from_millis(100));
45 }
46 })
47 });
48
49 let current_frame = frame_stream.get();
50
51 View::vstack()
52 .spacing(1)
53 .child(
54 View::styled_text("Canvas Examples (Kitty Graphics)")
55 .bold()
56 .build(),
57 )
58 .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
59 .child(View::text(""))
60 // Basic shapes demo
61 .child(View::text("Basic Shapes:"))
62 .child(
63 View::canvas()
64 .width(200)
65 .height(80)
66 .on_draw(|ctx| {
67 // Clear to dark background
68 ctx.clear(Color::Rgb {
69 r: 30,
70 g: 30,
71 b: 40,
72 });
73
74 // Draw some lines
75 ctx.line(10, 10, 190, 10, Color::Red);
76 ctx.line(10, 10, 10, 70, Color::Green);
77 ctx.line(10, 70, 190, 70, Color::Blue);
78 ctx.line(190, 10, 190, 70, Color::Yellow);
79
80 // Draw diagonal lines
81 ctx.line(10, 10, 190, 70, Color::Cyan);
82 ctx.line(10, 70, 190, 10, Color::Magenta);
83
84 // Draw filled rectangles
85 ctx.fill_rect(30, 25, 30, 20, Color::Red);
86 ctx.fill_rect(80, 25, 30, 20, Color::Green);
87 ctx.fill_rect(130, 25, 30, 20, Color::Blue);
88
89 // Draw stroked rectangles
90 ctx.stroke_rect(30, 50, 30, 15, Color::Yellow);
91 ctx.stroke_rect(80, 50, 30, 15, Color::Cyan);
92 ctx.stroke_rect(130, 50, 30, 15, Color::Magenta);
93 })
94 .build(),
95 )
96 .child(View::text(""))
97 // Circles demo
98 .child(View::text("Circles:"))
99 .child(
100 View::canvas()
101 .width(200)
102 .height(60)
103 .on_draw(|ctx| {
104 ctx.clear(Color::Rgb {
105 r: 20,
106 g: 25,
107 b: 35,
108 });
109
110 // Filled circles
111 ctx.fill_circle(30, 30, 20, Color::Red);
112 ctx.fill_circle(80, 30, 15, Color::Green);
113 ctx.fill_circle(120, 30, 10, Color::Blue);
114
115 // Stroked circles
116 ctx.circle(160, 30, 20, Color::Yellow);
117 ctx.circle(160, 30, 15, Color::Cyan);
118 ctx.circle(160, 30, 10, Color::Magenta);
119 })
120 .build(),
121 )
122 .child(View::text(""))
123 // Animated bar chart
124 .child(View::text("Animated Bar Chart:"))
125 .child(
126 View::canvas()
127 .width(200)
128 .height(80)
129 .on_draw({
130 move |ctx| {
131 ctx.clear(Color::Rgb {
132 r: 25,
133 g: 25,
134 b: 30,
135 });
136
137 // Generate animated data
138 let data: Vec<f32> = (0..8)
139 .map(|i| {
140 let phase = (current_frame as f32 * 0.1) + (i as f32 * 0.5);
141 0.3 + 0.7 * ((phase.sin() + 1.0) / 2.0)
142 })
143 .collect();
144
145 let bar_width = 20u16;
146 let gap = 5u16;
147 let max_height = 60u16;
148 let start_x = 10u16;
149 let baseline = 75u16;
150
151 // Draw baseline
152 ctx.line(5, baseline as i32, 195, baseline as i32, Color::Grey);
153
154 // Draw bars
155 let colors = [
156 Color::Red,
157 Color::Green,
158 Color::Blue,
159 Color::Yellow,
160 Color::Cyan,
161 Color::Magenta,
162 Color::Rgb {
163 r: 255,
164 g: 128,
165 b: 0,
166 },
167 Color::Rgb {
168 r: 128,
169 g: 255,
170 b: 128,
171 },
172 ];
173
174 for (i, &value) in data.iter().enumerate() {
175 let x = start_x + (i as u16) * (bar_width + gap);
176 let height = (value * max_height as f32) as u16;
177 let y = baseline - height;
178 ctx.fill_rect(x, y, bar_width, height, colors[i % colors.len()]);
179 }
180 }
181 })
182 .build(),
183 )
184 .child(View::text(""))
185 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
186 .child(
187 View::modal()
188 .visible(show_help.get())
189 .title("Example 29: Canvas")
190 .on_dismiss(with!(show_help => move || show_help.set(false)))
191 .child(
192 View::vstack()
193 .child(View::styled_text("What you're seeing").bold().build())
194 .child(View::text("• Pixel graphics via Kitty protocol"))
195 .child(View::text("• Lines, rectangles, circles"))
196 .child(View::text("• Animated bar chart"))
197 .child(View::gap(1))
198 .child(View::styled_text("Key concepts").bold().build())
199 .child(View::text("• View::canvas() creates drawing area"))
200 .child(View::text("• .on_draw(|ctx| { ... }) draws pixels"))
201 .child(View::text("• ctx.line(), ctx.fill_rect(), etc."))
202 .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
203 .child(View::gap(1))
204 .child(View::styled_text("Try this").bold().build())
205 .child(View::text("• Watch the animated bar chart"))
206 .child(View::text("• Run in compatible terminal"))
207 .child(View::gap(1))
208 .child(View::styled_text("Next up").bold().build())
209 .child(View::text("→ 30_image: image display"))
210 .child(View::gap(1))
211 .child(View::styled_text("Press Escape to close").dim().build())
212 .build(),
213 )
214 .build(),
215 )
216 .build()
217 }More examples
examples/31_animated_canvas.rs (line 114)
33 fn render(&self, cx: Scope) -> View {
34 let show_help = state!(cx, || false);
35
36 // F1 toggles help
37 cx.use_command(
38 KeyBinding::key(KeyCode::F(1)),
39 with!(show_help => move || show_help.update(|v| *v = !*v)),
40 );
41 View::vstack()
42 .spacing(1)
43 .child(
44 View::styled_text("Animated Canvas Demo (Kitty Graphics)")
45 .bold()
46 .build(),
47 )
48 .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
49 .child(View::text(""))
50 // Bouncing ball animation
51 .child(View::text("Bouncing Ball (60 FPS):"))
52 .child(
53 animated_canvas(cx.clone())
54 .width(200)
55 .height(60)
56 .fps(60)
57 .on_frame(|ctx, frame| {
58 ctx.clear(Color::Rgb {
59 r: 20,
60 g: 20,
61 b: 30,
62 });
63
64 // Ball physics
65 let t = frame as f32 * 0.05;
66 let x = 100.0 + 80.0 * t.cos();
67 let y = 30.0 + 20.0 * (t * 1.5).sin().abs();
68
69 // Shadow
70 ctx.fill_circle(
71 x as u16,
72 55,
73 8,
74 Color::Rgb {
75 r: 40,
76 g: 40,
77 b: 50,
78 },
79 );
80
81 // Ball with gradient effect (outer to inner)
82 ctx.fill_circle(
83 x as u16,
84 y as u16,
85 12,
86 Color::Rgb {
87 r: 200,
88 g: 50,
89 b: 50,
90 },
91 );
92 ctx.fill_circle(
93 x as u16,
94 y as u16,
95 8,
96 Color::Rgb {
97 r: 255,
98 g: 80,
99 b: 80,
100 },
101 );
102 ctx.fill_circle(
103 x as u16 - 2,
104 y as u16 - 2,
105 3,
106 Color::Rgb {
107 r: 255,
108 g: 200,
109 b: 200,
110 },
111 );
112
113 // Ground line
114 ctx.line(10, 58, 190, 58, Color::Grey);
115 })
116 .build(),
117 )
118 .child(View::text(""))
119 // Sine wave animation
120 .child(View::text("Sine Waves (30 FPS):"))
121 .child(
122 animated_canvas(cx.clone())
123 .width(200)
124 .height(50)
125 .fps(30)
126 .on_frame(|ctx, frame| {
127 ctx.clear(Color::Rgb {
128 r: 15,
129 g: 25,
130 b: 35,
131 });
132
133 let phase = frame as f32 * 0.1;
134
135 // Draw multiple sine waves
136 let colors = [
137 Color::Rgb {
138 r: 255,
139 g: 100,
140 b: 100,
141 },
142 Color::Rgb {
143 r: 100,
144 g: 255,
145 b: 100,
146 },
147 Color::Rgb {
148 r: 100,
149 g: 100,
150 b: 255,
151 },
152 ];
153
154 for (wave_idx, color) in colors.iter().enumerate() {
155 let offset = wave_idx as f32 * 0.5;
156 let amplitude = 15.0 - wave_idx as f32 * 3.0;
157
158 for x in 0..200 {
159 let t = x as f32 * 0.05 + phase + offset;
160 let y = 25.0 + amplitude * t.sin();
161 ctx.pixel(x, y as u16, *color);
162
163 // Make line thicker
164 if y as u16 > 0 {
165 ctx.pixel(x, y as u16 - 1, *color);
166 }
167 }
168 }
169
170 // Center line
171 ctx.line(0, 25, 199, 25, Color::DarkGrey);
172 })
173 .build(),
174 )
175 .child(View::text(""))
176 // Particle effect
177 .child(View::text("Particle Fountain (45 FPS):"))
178 .child(
179 animated_canvas(cx.clone())
180 .width(200)
181 .height(60)
182 .fps(45)
183 .on_frame(|ctx, frame| {
184 ctx.clear(Color::Rgb {
185 r: 10,
186 g: 10,
187 b: 20,
188 });
189
190 // Simple particle system using deterministic "random" based on frame
191 let num_particles = 30;
192 for i in 0..num_particles {
193 // Each particle has a lifecycle based on frame offset
194 let particle_frame = (frame + i * 7) % 60;
195 let life = particle_frame as f32 / 60.0;
196
197 // Starting position (center bottom)
198 let start_x = 100.0;
199 let start_y = 55.0;
200
201 // Velocity varies per particle (deterministic "random")
202 let angle = -1.57 + (i as f32 * 0.21).sin() * 0.8; // Around -90 degrees
203 let speed = 40.0 + (i as f32 * 0.37).cos() * 20.0;
204
205 // Physics: position = start + velocity * time + 0.5 * gravity * time^2
206 let vx = angle.cos() * speed;
207 let vy = angle.sin() * speed;
208 let gravity = 80.0;
209
210 let x = start_x + vx * life;
211 let y = start_y + vy * life + 0.5 * gravity * life * life;
212
213 // Fade out as particle ages
214 let alpha = (1.0 - life) * 255.0;
215
216 // Color based on age (yellow -> orange -> red)
217 let r = 255;
218 let g = ((1.0 - life * 0.7) * 200.0) as u8;
219 let b = ((1.0 - life) * 100.0) as u8;
220
221 if (0.0..200.0).contains(&x) && (0.0..60.0).contains(&y) {
222 let color = Color::Rgb { r, g, b };
223 ctx.pixel_alpha(x as u16, y as u16, color, alpha as u8);
224 // Make particles slightly larger
225 if x > 0.0 {
226 ctx.pixel_alpha(
227 x as u16 - 1,
228 y as u16,
229 color,
230 (alpha * 0.5) as u8,
231 );
232 }
233 if y > 0.0 {
234 ctx.pixel_alpha(
235 x as u16,
236 y as u16 - 1,
237 color,
238 (alpha * 0.5) as u8,
239 );
240 }
241 }
242 }
243
244 // Emitter glow
245 ctx.fill_circle(100, 55, 3, Color::Yellow);
246 })
247 .build(),
248 )
249 .child(View::text(""))
250 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
251 .child(
252 View::modal()
253 .visible(show_help.get())
254 .title("Example 31: Animated Canvas")
255 .on_dismiss(with!(show_help => move || show_help.set(false)))
256 .child(
257 View::vstack()
258 .child(View::styled_text("What you're seeing").bold().build())
259 .child(View::text("• Bouncing ball animation (60 FPS)"))
260 .child(View::text("• Sine wave visualization (30 FPS)"))
261 .child(View::text("• Particle fountain (45 FPS)"))
262 .child(View::gap(1))
263 .child(View::styled_text("Key concepts").bold().build())
264 .child(View::text("• animated_canvas(cx) helper"))
265 .child(View::text("• .fps() sets frame rate"))
266 .child(View::text("• .on_frame(|ctx, frame| { }) draws"))
267 .child(View::text("• Frame counter enables animation"))
268 .child(View::gap(1))
269 .child(View::styled_text("Try this").bold().build())
270 .child(View::text("• Watch the smooth animations"))
271 .child(View::text("• Notice different FPS rates"))
272 .child(View::gap(1))
273 .child(View::styled_text("Next up").bold().build())
274 .child(View::text("→ 32_effects: side effects"))
275 .child(View::gap(1))
276 .child(View::styled_text("Press Escape to close").dim().build())
277 .build(),
278 )
279 .build(),
280 )
281 .build()
282 }Sourcepub fn stroke_rect(&mut self, x: u16, y: u16, w: u16, h: u16, color: Color)
pub fn stroke_rect(&mut self, x: u16, y: u16, w: u16, h: u16, color: Color)
Draw a stroked rectangle (outline only).
Examples found in repository?
examples/29_canvas.rs (line 90)
31 fn render(&self, cx: Scope) -> View {
32 let show_help = state!(cx, || false);
33
34 // F1 toggles help
35 cx.use_command(
36 KeyBinding::key(KeyCode::F(1)),
37 with!(show_help => move || show_help.update(|v| *v = !*v)),
38 );
39
40 // Animated value for the bar chart using stream macro
41 let frame_stream = stream!(cx, || {
42 (0u32..).inspect(|&i| {
43 if i > 0 {
44 std::thread::sleep(std::time::Duration::from_millis(100));
45 }
46 })
47 });
48
49 let current_frame = frame_stream.get();
50
51 View::vstack()
52 .spacing(1)
53 .child(
54 View::styled_text("Canvas Examples (Kitty Graphics)")
55 .bold()
56 .build(),
57 )
58 .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
59 .child(View::text(""))
60 // Basic shapes demo
61 .child(View::text("Basic Shapes:"))
62 .child(
63 View::canvas()
64 .width(200)
65 .height(80)
66 .on_draw(|ctx| {
67 // Clear to dark background
68 ctx.clear(Color::Rgb {
69 r: 30,
70 g: 30,
71 b: 40,
72 });
73
74 // Draw some lines
75 ctx.line(10, 10, 190, 10, Color::Red);
76 ctx.line(10, 10, 10, 70, Color::Green);
77 ctx.line(10, 70, 190, 70, Color::Blue);
78 ctx.line(190, 10, 190, 70, Color::Yellow);
79
80 // Draw diagonal lines
81 ctx.line(10, 10, 190, 70, Color::Cyan);
82 ctx.line(10, 70, 190, 10, Color::Magenta);
83
84 // Draw filled rectangles
85 ctx.fill_rect(30, 25, 30, 20, Color::Red);
86 ctx.fill_rect(80, 25, 30, 20, Color::Green);
87 ctx.fill_rect(130, 25, 30, 20, Color::Blue);
88
89 // Draw stroked rectangles
90 ctx.stroke_rect(30, 50, 30, 15, Color::Yellow);
91 ctx.stroke_rect(80, 50, 30, 15, Color::Cyan);
92 ctx.stroke_rect(130, 50, 30, 15, Color::Magenta);
93 })
94 .build(),
95 )
96 .child(View::text(""))
97 // Circles demo
98 .child(View::text("Circles:"))
99 .child(
100 View::canvas()
101 .width(200)
102 .height(60)
103 .on_draw(|ctx| {
104 ctx.clear(Color::Rgb {
105 r: 20,
106 g: 25,
107 b: 35,
108 });
109
110 // Filled circles
111 ctx.fill_circle(30, 30, 20, Color::Red);
112 ctx.fill_circle(80, 30, 15, Color::Green);
113 ctx.fill_circle(120, 30, 10, Color::Blue);
114
115 // Stroked circles
116 ctx.circle(160, 30, 20, Color::Yellow);
117 ctx.circle(160, 30, 15, Color::Cyan);
118 ctx.circle(160, 30, 10, Color::Magenta);
119 })
120 .build(),
121 )
122 .child(View::text(""))
123 // Animated bar chart
124 .child(View::text("Animated Bar Chart:"))
125 .child(
126 View::canvas()
127 .width(200)
128 .height(80)
129 .on_draw({
130 move |ctx| {
131 ctx.clear(Color::Rgb {
132 r: 25,
133 g: 25,
134 b: 30,
135 });
136
137 // Generate animated data
138 let data: Vec<f32> = (0..8)
139 .map(|i| {
140 let phase = (current_frame as f32 * 0.1) + (i as f32 * 0.5);
141 0.3 + 0.7 * ((phase.sin() + 1.0) / 2.0)
142 })
143 .collect();
144
145 let bar_width = 20u16;
146 let gap = 5u16;
147 let max_height = 60u16;
148 let start_x = 10u16;
149 let baseline = 75u16;
150
151 // Draw baseline
152 ctx.line(5, baseline as i32, 195, baseline as i32, Color::Grey);
153
154 // Draw bars
155 let colors = [
156 Color::Red,
157 Color::Green,
158 Color::Blue,
159 Color::Yellow,
160 Color::Cyan,
161 Color::Magenta,
162 Color::Rgb {
163 r: 255,
164 g: 128,
165 b: 0,
166 },
167 Color::Rgb {
168 r: 128,
169 g: 255,
170 b: 128,
171 },
172 ];
173
174 for (i, &value) in data.iter().enumerate() {
175 let x = start_x + (i as u16) * (bar_width + gap);
176 let height = (value * max_height as f32) as u16;
177 let y = baseline - height;
178 ctx.fill_rect(x, y, bar_width, height, colors[i % colors.len()]);
179 }
180 }
181 })
182 .build(),
183 )
184 .child(View::text(""))
185 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
186 .child(
187 View::modal()
188 .visible(show_help.get())
189 .title("Example 29: Canvas")
190 .on_dismiss(with!(show_help => move || show_help.set(false)))
191 .child(
192 View::vstack()
193 .child(View::styled_text("What you're seeing").bold().build())
194 .child(View::text("• Pixel graphics via Kitty protocol"))
195 .child(View::text("• Lines, rectangles, circles"))
196 .child(View::text("• Animated bar chart"))
197 .child(View::gap(1))
198 .child(View::styled_text("Key concepts").bold().build())
199 .child(View::text("• View::canvas() creates drawing area"))
200 .child(View::text("• .on_draw(|ctx| { ... }) draws pixels"))
201 .child(View::text("• ctx.line(), ctx.fill_rect(), etc."))
202 .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
203 .child(View::gap(1))
204 .child(View::styled_text("Try this").bold().build())
205 .child(View::text("• Watch the animated bar chart"))
206 .child(View::text("• Run in compatible terminal"))
207 .child(View::gap(1))
208 .child(View::styled_text("Next up").bold().build())
209 .child(View::text("→ 30_image: image display"))
210 .child(View::gap(1))
211 .child(View::styled_text("Press Escape to close").dim().build())
212 .build(),
213 )
214 .build(),
215 )
216 .build()
217 }Sourcepub fn fill_rect(&mut self, x: u16, y: u16, w: u16, h: u16, color: Color)
pub fn fill_rect(&mut self, x: u16, y: u16, w: u16, h: u16, color: Color)
Draw a filled rectangle.
Examples found in repository?
examples/29_canvas.rs (line 85)
31 fn render(&self, cx: Scope) -> View {
32 let show_help = state!(cx, || false);
33
34 // F1 toggles help
35 cx.use_command(
36 KeyBinding::key(KeyCode::F(1)),
37 with!(show_help => move || show_help.update(|v| *v = !*v)),
38 );
39
40 // Animated value for the bar chart using stream macro
41 let frame_stream = stream!(cx, || {
42 (0u32..).inspect(|&i| {
43 if i > 0 {
44 std::thread::sleep(std::time::Duration::from_millis(100));
45 }
46 })
47 });
48
49 let current_frame = frame_stream.get();
50
51 View::vstack()
52 .spacing(1)
53 .child(
54 View::styled_text("Canvas Examples (Kitty Graphics)")
55 .bold()
56 .build(),
57 )
58 .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
59 .child(View::text(""))
60 // Basic shapes demo
61 .child(View::text("Basic Shapes:"))
62 .child(
63 View::canvas()
64 .width(200)
65 .height(80)
66 .on_draw(|ctx| {
67 // Clear to dark background
68 ctx.clear(Color::Rgb {
69 r: 30,
70 g: 30,
71 b: 40,
72 });
73
74 // Draw some lines
75 ctx.line(10, 10, 190, 10, Color::Red);
76 ctx.line(10, 10, 10, 70, Color::Green);
77 ctx.line(10, 70, 190, 70, Color::Blue);
78 ctx.line(190, 10, 190, 70, Color::Yellow);
79
80 // Draw diagonal lines
81 ctx.line(10, 10, 190, 70, Color::Cyan);
82 ctx.line(10, 70, 190, 10, Color::Magenta);
83
84 // Draw filled rectangles
85 ctx.fill_rect(30, 25, 30, 20, Color::Red);
86 ctx.fill_rect(80, 25, 30, 20, Color::Green);
87 ctx.fill_rect(130, 25, 30, 20, Color::Blue);
88
89 // Draw stroked rectangles
90 ctx.stroke_rect(30, 50, 30, 15, Color::Yellow);
91 ctx.stroke_rect(80, 50, 30, 15, Color::Cyan);
92 ctx.stroke_rect(130, 50, 30, 15, Color::Magenta);
93 })
94 .build(),
95 )
96 .child(View::text(""))
97 // Circles demo
98 .child(View::text("Circles:"))
99 .child(
100 View::canvas()
101 .width(200)
102 .height(60)
103 .on_draw(|ctx| {
104 ctx.clear(Color::Rgb {
105 r: 20,
106 g: 25,
107 b: 35,
108 });
109
110 // Filled circles
111 ctx.fill_circle(30, 30, 20, Color::Red);
112 ctx.fill_circle(80, 30, 15, Color::Green);
113 ctx.fill_circle(120, 30, 10, Color::Blue);
114
115 // Stroked circles
116 ctx.circle(160, 30, 20, Color::Yellow);
117 ctx.circle(160, 30, 15, Color::Cyan);
118 ctx.circle(160, 30, 10, Color::Magenta);
119 })
120 .build(),
121 )
122 .child(View::text(""))
123 // Animated bar chart
124 .child(View::text("Animated Bar Chart:"))
125 .child(
126 View::canvas()
127 .width(200)
128 .height(80)
129 .on_draw({
130 move |ctx| {
131 ctx.clear(Color::Rgb {
132 r: 25,
133 g: 25,
134 b: 30,
135 });
136
137 // Generate animated data
138 let data: Vec<f32> = (0..8)
139 .map(|i| {
140 let phase = (current_frame as f32 * 0.1) + (i as f32 * 0.5);
141 0.3 + 0.7 * ((phase.sin() + 1.0) / 2.0)
142 })
143 .collect();
144
145 let bar_width = 20u16;
146 let gap = 5u16;
147 let max_height = 60u16;
148 let start_x = 10u16;
149 let baseline = 75u16;
150
151 // Draw baseline
152 ctx.line(5, baseline as i32, 195, baseline as i32, Color::Grey);
153
154 // Draw bars
155 let colors = [
156 Color::Red,
157 Color::Green,
158 Color::Blue,
159 Color::Yellow,
160 Color::Cyan,
161 Color::Magenta,
162 Color::Rgb {
163 r: 255,
164 g: 128,
165 b: 0,
166 },
167 Color::Rgb {
168 r: 128,
169 g: 255,
170 b: 128,
171 },
172 ];
173
174 for (i, &value) in data.iter().enumerate() {
175 let x = start_x + (i as u16) * (bar_width + gap);
176 let height = (value * max_height as f32) as u16;
177 let y = baseline - height;
178 ctx.fill_rect(x, y, bar_width, height, colors[i % colors.len()]);
179 }
180 }
181 })
182 .build(),
183 )
184 .child(View::text(""))
185 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
186 .child(
187 View::modal()
188 .visible(show_help.get())
189 .title("Example 29: Canvas")
190 .on_dismiss(with!(show_help => move || show_help.set(false)))
191 .child(
192 View::vstack()
193 .child(View::styled_text("What you're seeing").bold().build())
194 .child(View::text("• Pixel graphics via Kitty protocol"))
195 .child(View::text("• Lines, rectangles, circles"))
196 .child(View::text("• Animated bar chart"))
197 .child(View::gap(1))
198 .child(View::styled_text("Key concepts").bold().build())
199 .child(View::text("• View::canvas() creates drawing area"))
200 .child(View::text("• .on_draw(|ctx| { ... }) draws pixels"))
201 .child(View::text("• ctx.line(), ctx.fill_rect(), etc."))
202 .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
203 .child(View::gap(1))
204 .child(View::styled_text("Try this").bold().build())
205 .child(View::text("• Watch the animated bar chart"))
206 .child(View::text("• Run in compatible terminal"))
207 .child(View::gap(1))
208 .child(View::styled_text("Next up").bold().build())
209 .child(View::text("→ 30_image: image display"))
210 .child(View::gap(1))
211 .child(View::styled_text("Press Escape to close").dim().build())
212 .build(),
213 )
214 .build(),
215 )
216 .build()
217 }Sourcepub fn circle(&mut self, cx: u16, cy: u16, radius: u16, color: Color)
pub fn circle(&mut self, cx: u16, cy: u16, radius: u16, color: Color)
Draw a stroked circle (outline only) using midpoint algorithm.
Examples found in repository?
examples/29_canvas.rs (line 116)
31 fn render(&self, cx: Scope) -> View {
32 let show_help = state!(cx, || false);
33
34 // F1 toggles help
35 cx.use_command(
36 KeyBinding::key(KeyCode::F(1)),
37 with!(show_help => move || show_help.update(|v| *v = !*v)),
38 );
39
40 // Animated value for the bar chart using stream macro
41 let frame_stream = stream!(cx, || {
42 (0u32..).inspect(|&i| {
43 if i > 0 {
44 std::thread::sleep(std::time::Duration::from_millis(100));
45 }
46 })
47 });
48
49 let current_frame = frame_stream.get();
50
51 View::vstack()
52 .spacing(1)
53 .child(
54 View::styled_text("Canvas Examples (Kitty Graphics)")
55 .bold()
56 .build(),
57 )
58 .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
59 .child(View::text(""))
60 // Basic shapes demo
61 .child(View::text("Basic Shapes:"))
62 .child(
63 View::canvas()
64 .width(200)
65 .height(80)
66 .on_draw(|ctx| {
67 // Clear to dark background
68 ctx.clear(Color::Rgb {
69 r: 30,
70 g: 30,
71 b: 40,
72 });
73
74 // Draw some lines
75 ctx.line(10, 10, 190, 10, Color::Red);
76 ctx.line(10, 10, 10, 70, Color::Green);
77 ctx.line(10, 70, 190, 70, Color::Blue);
78 ctx.line(190, 10, 190, 70, Color::Yellow);
79
80 // Draw diagonal lines
81 ctx.line(10, 10, 190, 70, Color::Cyan);
82 ctx.line(10, 70, 190, 10, Color::Magenta);
83
84 // Draw filled rectangles
85 ctx.fill_rect(30, 25, 30, 20, Color::Red);
86 ctx.fill_rect(80, 25, 30, 20, Color::Green);
87 ctx.fill_rect(130, 25, 30, 20, Color::Blue);
88
89 // Draw stroked rectangles
90 ctx.stroke_rect(30, 50, 30, 15, Color::Yellow);
91 ctx.stroke_rect(80, 50, 30, 15, Color::Cyan);
92 ctx.stroke_rect(130, 50, 30, 15, Color::Magenta);
93 })
94 .build(),
95 )
96 .child(View::text(""))
97 // Circles demo
98 .child(View::text("Circles:"))
99 .child(
100 View::canvas()
101 .width(200)
102 .height(60)
103 .on_draw(|ctx| {
104 ctx.clear(Color::Rgb {
105 r: 20,
106 g: 25,
107 b: 35,
108 });
109
110 // Filled circles
111 ctx.fill_circle(30, 30, 20, Color::Red);
112 ctx.fill_circle(80, 30, 15, Color::Green);
113 ctx.fill_circle(120, 30, 10, Color::Blue);
114
115 // Stroked circles
116 ctx.circle(160, 30, 20, Color::Yellow);
117 ctx.circle(160, 30, 15, Color::Cyan);
118 ctx.circle(160, 30, 10, Color::Magenta);
119 })
120 .build(),
121 )
122 .child(View::text(""))
123 // Animated bar chart
124 .child(View::text("Animated Bar Chart:"))
125 .child(
126 View::canvas()
127 .width(200)
128 .height(80)
129 .on_draw({
130 move |ctx| {
131 ctx.clear(Color::Rgb {
132 r: 25,
133 g: 25,
134 b: 30,
135 });
136
137 // Generate animated data
138 let data: Vec<f32> = (0..8)
139 .map(|i| {
140 let phase = (current_frame as f32 * 0.1) + (i as f32 * 0.5);
141 0.3 + 0.7 * ((phase.sin() + 1.0) / 2.0)
142 })
143 .collect();
144
145 let bar_width = 20u16;
146 let gap = 5u16;
147 let max_height = 60u16;
148 let start_x = 10u16;
149 let baseline = 75u16;
150
151 // Draw baseline
152 ctx.line(5, baseline as i32, 195, baseline as i32, Color::Grey);
153
154 // Draw bars
155 let colors = [
156 Color::Red,
157 Color::Green,
158 Color::Blue,
159 Color::Yellow,
160 Color::Cyan,
161 Color::Magenta,
162 Color::Rgb {
163 r: 255,
164 g: 128,
165 b: 0,
166 },
167 Color::Rgb {
168 r: 128,
169 g: 255,
170 b: 128,
171 },
172 ];
173
174 for (i, &value) in data.iter().enumerate() {
175 let x = start_x + (i as u16) * (bar_width + gap);
176 let height = (value * max_height as f32) as u16;
177 let y = baseline - height;
178 ctx.fill_rect(x, y, bar_width, height, colors[i % colors.len()]);
179 }
180 }
181 })
182 .build(),
183 )
184 .child(View::text(""))
185 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
186 .child(
187 View::modal()
188 .visible(show_help.get())
189 .title("Example 29: Canvas")
190 .on_dismiss(with!(show_help => move || show_help.set(false)))
191 .child(
192 View::vstack()
193 .child(View::styled_text("What you're seeing").bold().build())
194 .child(View::text("• Pixel graphics via Kitty protocol"))
195 .child(View::text("• Lines, rectangles, circles"))
196 .child(View::text("• Animated bar chart"))
197 .child(View::gap(1))
198 .child(View::styled_text("Key concepts").bold().build())
199 .child(View::text("• View::canvas() creates drawing area"))
200 .child(View::text("• .on_draw(|ctx| { ... }) draws pixels"))
201 .child(View::text("• ctx.line(), ctx.fill_rect(), etc."))
202 .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
203 .child(View::gap(1))
204 .child(View::styled_text("Try this").bold().build())
205 .child(View::text("• Watch the animated bar chart"))
206 .child(View::text("• Run in compatible terminal"))
207 .child(View::gap(1))
208 .child(View::styled_text("Next up").bold().build())
209 .child(View::text("→ 30_image: image display"))
210 .child(View::gap(1))
211 .child(View::styled_text("Press Escape to close").dim().build())
212 .build(),
213 )
214 .build(),
215 )
216 .build()
217 }Sourcepub fn fill_circle(&mut self, cx: u16, cy: u16, radius: u16, color: Color)
pub fn fill_circle(&mut self, cx: u16, cy: u16, radius: u16, color: Color)
Draw a filled circle.
Examples found in repository?
examples/29_canvas.rs (line 111)
31 fn render(&self, cx: Scope) -> View {
32 let show_help = state!(cx, || false);
33
34 // F1 toggles help
35 cx.use_command(
36 KeyBinding::key(KeyCode::F(1)),
37 with!(show_help => move || show_help.update(|v| *v = !*v)),
38 );
39
40 // Animated value for the bar chart using stream macro
41 let frame_stream = stream!(cx, || {
42 (0u32..).inspect(|&i| {
43 if i > 0 {
44 std::thread::sleep(std::time::Duration::from_millis(100));
45 }
46 })
47 });
48
49 let current_frame = frame_stream.get();
50
51 View::vstack()
52 .spacing(1)
53 .child(
54 View::styled_text("Canvas Examples (Kitty Graphics)")
55 .bold()
56 .build(),
57 )
58 .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
59 .child(View::text(""))
60 // Basic shapes demo
61 .child(View::text("Basic Shapes:"))
62 .child(
63 View::canvas()
64 .width(200)
65 .height(80)
66 .on_draw(|ctx| {
67 // Clear to dark background
68 ctx.clear(Color::Rgb {
69 r: 30,
70 g: 30,
71 b: 40,
72 });
73
74 // Draw some lines
75 ctx.line(10, 10, 190, 10, Color::Red);
76 ctx.line(10, 10, 10, 70, Color::Green);
77 ctx.line(10, 70, 190, 70, Color::Blue);
78 ctx.line(190, 10, 190, 70, Color::Yellow);
79
80 // Draw diagonal lines
81 ctx.line(10, 10, 190, 70, Color::Cyan);
82 ctx.line(10, 70, 190, 10, Color::Magenta);
83
84 // Draw filled rectangles
85 ctx.fill_rect(30, 25, 30, 20, Color::Red);
86 ctx.fill_rect(80, 25, 30, 20, Color::Green);
87 ctx.fill_rect(130, 25, 30, 20, Color::Blue);
88
89 // Draw stroked rectangles
90 ctx.stroke_rect(30, 50, 30, 15, Color::Yellow);
91 ctx.stroke_rect(80, 50, 30, 15, Color::Cyan);
92 ctx.stroke_rect(130, 50, 30, 15, Color::Magenta);
93 })
94 .build(),
95 )
96 .child(View::text(""))
97 // Circles demo
98 .child(View::text("Circles:"))
99 .child(
100 View::canvas()
101 .width(200)
102 .height(60)
103 .on_draw(|ctx| {
104 ctx.clear(Color::Rgb {
105 r: 20,
106 g: 25,
107 b: 35,
108 });
109
110 // Filled circles
111 ctx.fill_circle(30, 30, 20, Color::Red);
112 ctx.fill_circle(80, 30, 15, Color::Green);
113 ctx.fill_circle(120, 30, 10, Color::Blue);
114
115 // Stroked circles
116 ctx.circle(160, 30, 20, Color::Yellow);
117 ctx.circle(160, 30, 15, Color::Cyan);
118 ctx.circle(160, 30, 10, Color::Magenta);
119 })
120 .build(),
121 )
122 .child(View::text(""))
123 // Animated bar chart
124 .child(View::text("Animated Bar Chart:"))
125 .child(
126 View::canvas()
127 .width(200)
128 .height(80)
129 .on_draw({
130 move |ctx| {
131 ctx.clear(Color::Rgb {
132 r: 25,
133 g: 25,
134 b: 30,
135 });
136
137 // Generate animated data
138 let data: Vec<f32> = (0..8)
139 .map(|i| {
140 let phase = (current_frame as f32 * 0.1) + (i as f32 * 0.5);
141 0.3 + 0.7 * ((phase.sin() + 1.0) / 2.0)
142 })
143 .collect();
144
145 let bar_width = 20u16;
146 let gap = 5u16;
147 let max_height = 60u16;
148 let start_x = 10u16;
149 let baseline = 75u16;
150
151 // Draw baseline
152 ctx.line(5, baseline as i32, 195, baseline as i32, Color::Grey);
153
154 // Draw bars
155 let colors = [
156 Color::Red,
157 Color::Green,
158 Color::Blue,
159 Color::Yellow,
160 Color::Cyan,
161 Color::Magenta,
162 Color::Rgb {
163 r: 255,
164 g: 128,
165 b: 0,
166 },
167 Color::Rgb {
168 r: 128,
169 g: 255,
170 b: 128,
171 },
172 ];
173
174 for (i, &value) in data.iter().enumerate() {
175 let x = start_x + (i as u16) * (bar_width + gap);
176 let height = (value * max_height as f32) as u16;
177 let y = baseline - height;
178 ctx.fill_rect(x, y, bar_width, height, colors[i % colors.len()]);
179 }
180 }
181 })
182 .build(),
183 )
184 .child(View::text(""))
185 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
186 .child(
187 View::modal()
188 .visible(show_help.get())
189 .title("Example 29: Canvas")
190 .on_dismiss(with!(show_help => move || show_help.set(false)))
191 .child(
192 View::vstack()
193 .child(View::styled_text("What you're seeing").bold().build())
194 .child(View::text("• Pixel graphics via Kitty protocol"))
195 .child(View::text("• Lines, rectangles, circles"))
196 .child(View::text("• Animated bar chart"))
197 .child(View::gap(1))
198 .child(View::styled_text("Key concepts").bold().build())
199 .child(View::text("• View::canvas() creates drawing area"))
200 .child(View::text("• .on_draw(|ctx| { ... }) draws pixels"))
201 .child(View::text("• ctx.line(), ctx.fill_rect(), etc."))
202 .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
203 .child(View::gap(1))
204 .child(View::styled_text("Try this").bold().build())
205 .child(View::text("• Watch the animated bar chart"))
206 .child(View::text("• Run in compatible terminal"))
207 .child(View::gap(1))
208 .child(View::styled_text("Next up").bold().build())
209 .child(View::text("→ 30_image: image display"))
210 .child(View::gap(1))
211 .child(View::styled_text("Press Escape to close").dim().build())
212 .build(),
213 )
214 .build(),
215 )
216 .build()
217 }More examples
examples/31_animated_canvas.rs (lines 70-79)
33 fn render(&self, cx: Scope) -> View {
34 let show_help = state!(cx, || false);
35
36 // F1 toggles help
37 cx.use_command(
38 KeyBinding::key(KeyCode::F(1)),
39 with!(show_help => move || show_help.update(|v| *v = !*v)),
40 );
41 View::vstack()
42 .spacing(1)
43 .child(
44 View::styled_text("Animated Canvas Demo (Kitty Graphics)")
45 .bold()
46 .build(),
47 )
48 .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
49 .child(View::text(""))
50 // Bouncing ball animation
51 .child(View::text("Bouncing Ball (60 FPS):"))
52 .child(
53 animated_canvas(cx.clone())
54 .width(200)
55 .height(60)
56 .fps(60)
57 .on_frame(|ctx, frame| {
58 ctx.clear(Color::Rgb {
59 r: 20,
60 g: 20,
61 b: 30,
62 });
63
64 // Ball physics
65 let t = frame as f32 * 0.05;
66 let x = 100.0 + 80.0 * t.cos();
67 let y = 30.0 + 20.0 * (t * 1.5).sin().abs();
68
69 // Shadow
70 ctx.fill_circle(
71 x as u16,
72 55,
73 8,
74 Color::Rgb {
75 r: 40,
76 g: 40,
77 b: 50,
78 },
79 );
80
81 // Ball with gradient effect (outer to inner)
82 ctx.fill_circle(
83 x as u16,
84 y as u16,
85 12,
86 Color::Rgb {
87 r: 200,
88 g: 50,
89 b: 50,
90 },
91 );
92 ctx.fill_circle(
93 x as u16,
94 y as u16,
95 8,
96 Color::Rgb {
97 r: 255,
98 g: 80,
99 b: 80,
100 },
101 );
102 ctx.fill_circle(
103 x as u16 - 2,
104 y as u16 - 2,
105 3,
106 Color::Rgb {
107 r: 255,
108 g: 200,
109 b: 200,
110 },
111 );
112
113 // Ground line
114 ctx.line(10, 58, 190, 58, Color::Grey);
115 })
116 .build(),
117 )
118 .child(View::text(""))
119 // Sine wave animation
120 .child(View::text("Sine Waves (30 FPS):"))
121 .child(
122 animated_canvas(cx.clone())
123 .width(200)
124 .height(50)
125 .fps(30)
126 .on_frame(|ctx, frame| {
127 ctx.clear(Color::Rgb {
128 r: 15,
129 g: 25,
130 b: 35,
131 });
132
133 let phase = frame as f32 * 0.1;
134
135 // Draw multiple sine waves
136 let colors = [
137 Color::Rgb {
138 r: 255,
139 g: 100,
140 b: 100,
141 },
142 Color::Rgb {
143 r: 100,
144 g: 255,
145 b: 100,
146 },
147 Color::Rgb {
148 r: 100,
149 g: 100,
150 b: 255,
151 },
152 ];
153
154 for (wave_idx, color) in colors.iter().enumerate() {
155 let offset = wave_idx as f32 * 0.5;
156 let amplitude = 15.0 - wave_idx as f32 * 3.0;
157
158 for x in 0..200 {
159 let t = x as f32 * 0.05 + phase + offset;
160 let y = 25.0 + amplitude * t.sin();
161 ctx.pixel(x, y as u16, *color);
162
163 // Make line thicker
164 if y as u16 > 0 {
165 ctx.pixel(x, y as u16 - 1, *color);
166 }
167 }
168 }
169
170 // Center line
171 ctx.line(0, 25, 199, 25, Color::DarkGrey);
172 })
173 .build(),
174 )
175 .child(View::text(""))
176 // Particle effect
177 .child(View::text("Particle Fountain (45 FPS):"))
178 .child(
179 animated_canvas(cx.clone())
180 .width(200)
181 .height(60)
182 .fps(45)
183 .on_frame(|ctx, frame| {
184 ctx.clear(Color::Rgb {
185 r: 10,
186 g: 10,
187 b: 20,
188 });
189
190 // Simple particle system using deterministic "random" based on frame
191 let num_particles = 30;
192 for i in 0..num_particles {
193 // Each particle has a lifecycle based on frame offset
194 let particle_frame = (frame + i * 7) % 60;
195 let life = particle_frame as f32 / 60.0;
196
197 // Starting position (center bottom)
198 let start_x = 100.0;
199 let start_y = 55.0;
200
201 // Velocity varies per particle (deterministic "random")
202 let angle = -1.57 + (i as f32 * 0.21).sin() * 0.8; // Around -90 degrees
203 let speed = 40.0 + (i as f32 * 0.37).cos() * 20.0;
204
205 // Physics: position = start + velocity * time + 0.5 * gravity * time^2
206 let vx = angle.cos() * speed;
207 let vy = angle.sin() * speed;
208 let gravity = 80.0;
209
210 let x = start_x + vx * life;
211 let y = start_y + vy * life + 0.5 * gravity * life * life;
212
213 // Fade out as particle ages
214 let alpha = (1.0 - life) * 255.0;
215
216 // Color based on age (yellow -> orange -> red)
217 let r = 255;
218 let g = ((1.0 - life * 0.7) * 200.0) as u8;
219 let b = ((1.0 - life) * 100.0) as u8;
220
221 if (0.0..200.0).contains(&x) && (0.0..60.0).contains(&y) {
222 let color = Color::Rgb { r, g, b };
223 ctx.pixel_alpha(x as u16, y as u16, color, alpha as u8);
224 // Make particles slightly larger
225 if x > 0.0 {
226 ctx.pixel_alpha(
227 x as u16 - 1,
228 y as u16,
229 color,
230 (alpha * 0.5) as u8,
231 );
232 }
233 if y > 0.0 {
234 ctx.pixel_alpha(
235 x as u16,
236 y as u16 - 1,
237 color,
238 (alpha * 0.5) as u8,
239 );
240 }
241 }
242 }
243
244 // Emitter glow
245 ctx.fill_circle(100, 55, 3, Color::Yellow);
246 })
247 .build(),
248 )
249 .child(View::text(""))
250 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
251 .child(
252 View::modal()
253 .visible(show_help.get())
254 .title("Example 31: Animated Canvas")
255 .on_dismiss(with!(show_help => move || show_help.set(false)))
256 .child(
257 View::vstack()
258 .child(View::styled_text("What you're seeing").bold().build())
259 .child(View::text("• Bouncing ball animation (60 FPS)"))
260 .child(View::text("• Sine wave visualization (30 FPS)"))
261 .child(View::text("• Particle fountain (45 FPS)"))
262 .child(View::gap(1))
263 .child(View::styled_text("Key concepts").bold().build())
264 .child(View::text("• animated_canvas(cx) helper"))
265 .child(View::text("• .fps() sets frame rate"))
266 .child(View::text("• .on_frame(|ctx, frame| { }) draws"))
267 .child(View::text("• Frame counter enables animation"))
268 .child(View::gap(1))
269 .child(View::styled_text("Try this").bold().build())
270 .child(View::text("• Watch the smooth animations"))
271 .child(View::text("• Notice different FPS rates"))
272 .child(View::gap(1))
273 .child(View::styled_text("Next up").bold().build())
274 .child(View::text("→ 32_effects: side effects"))
275 .child(View::gap(1))
276 .child(View::styled_text("Press Escape to close").dim().build())
277 .build(),
278 )
279 .build(),
280 )
281 .build()
282 }Auto Trait Implementations§
impl<'a> Freeze for DrawContext<'a>
impl<'a> RefUnwindSafe for DrawContext<'a>
impl<'a> Send for DrawContext<'a>
impl<'a> Sync for DrawContext<'a>
impl<'a> Unpin for DrawContext<'a>
impl<'a> !UnwindSafe for DrawContext<'a>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.