Chapter 37: Canvas Fill and Stroke
Canvas Fill, Stroke, and Lines in a very clear, patient, step-by-step way.
Imagine I’m right next to you with the code editor open. We’ll build understanding slowly, with tiny complete examples you can copy-paste immediately. No rushing, no magic — just plain English explanations, visual thinking, and repeated patterns until everything feels natural.
Three most important mental models before we start coding
- Canvas is a painter, not a scene graph You don’t create “a line object” or “a filled circle object” that lives forever (like in SVG). You tell the painter: “use this color, this thickness… now paint pixels right now”. After fill() or stroke() the pixels are just pixels — there is no editable object anymore.
- Fill = inside the shapeStroke = the outline / border of the shape
- Every interesting shape in Canvas is a path Even a simple rectangle or circle is internally a path. You define the path first → then decide whether to fill() it, stroke() it, or both.
1. The absolute smallest Fill + Stroke example
Copy-paste and run this — it shows everything in one tiny picture.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Canvas – Fill & Stroke Basics</title> <style> canvas { border: 1px solid #ddd; background: #f8f9fa; display: block; margin: 40px auto; } </style> </head> <body> <canvas id="c" width="640" height="360"></canvas> <script> const canvas = document.getElementById('c'); const ctx = canvas.getContext('2d'); // ────────────── First shape: only fill (no border) ctx.fillStyle = '#4CAF50'; // paint bucket color ctx.fillRect(80, 80, 200, 140); // x, y, width, height // ────────────── Second shape: only stroke (no fill) ctx.strokeStyle = '#F44336'; // pen color ctx.lineWidth = 12; // pen thickness ctx.strokeRect(340, 80, 200, 140); // ────────────── Third shape: both fill + stroke ctx.beginPath(); ctx.arc(320, 260, 80, 0, Math.PI * 2); // circle at center bottom ctx.fillStyle = '#2196F3'; // inside color ctx.fill(); // paint inside first ctx.strokeStyle = '#0D47A1'; // border color ctx.lineWidth = 10; ctx.stroke(); // paint outline last // Label them ctx.font = '20px Arial'; ctx.fillStyle = '#333'; ctx.textAlign = 'center'; ctx.fillText('Only fill', 180, 240); ctx.fillText('Only stroke', 440, 240); ctx.fillText('Fill + stroke',320, 380); </script> </body> </html> |
What you should see & remember forever:
- Green rectangle → filled only (fillRect = fill + auto path)
- Red rectangle → outlined only (strokeRect = stroke + auto path)
- Blue circle → filled AND outlined (we defined path manually → fill() → stroke())
Important order rule Usually do fill() before stroke() — because stroke draws on top of fill.
2. Lines – the foundation of almost everything
Lines are the simplest path — but they teach you the pattern you’ll use for every shape.
Four-step recipe (you will type this 1000 times):
|
0 1 2 3 4 5 6 7 8 9 |
ctx.beginPath(); // 1. Start fresh path ctx.moveTo(100, 100); // 2. Jump to start (no line yet) ctx.lineTo(500, 300); // 3. Draw line to end point ctx.stroke(); // 4. Paint the line |
3. Full line properties example (copy-paste)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<canvas id="lines" width="600" height="400"></canvas> <script> const canvas = document.getElementById('lines'); const ctx = canvas.getContext('2d'); // ──── Line 1: basic solid black ctx.beginPath(); ctx.moveTo(80, 80); ctx.lineTo(520, 80); ctx.strokeStyle = '#333'; ctx.lineWidth = 8; ctx.stroke(); // ──── Line 2: thick blue + round ends ctx.beginPath(); ctx.moveTo(80, 140); ctx.lineTo(520, 140); ctx.strokeStyle = '#2196F3'; ctx.lineWidth = 24; ctx.lineCap = 'round'; // round / butt / square ctx.stroke(); // ──── Line 3: dashed orange ctx.beginPath(); ctx.moveTo(80, 220); ctx.lineTo(520, 220); ctx.strokeStyle = '#FF9800'; ctx.lineWidth = 10; ctx.setLineDash([15, 10]); // dash 15px, gap 10px ctx.stroke(); // ──── Line 4: dotted red + round caps ctx.beginPath(); ctx.moveTo(80, 280); ctx.lineTo(520, 280); ctx.strokeStyle = '#F44336'; ctx.lineWidth = 10; ctx.setLineDash([2, 12]); // very short dash = dot ctx.lineCap = 'round'; // makes nice round dots ctx.stroke(); // Reset dash pattern (important!) ctx.setLineDash([]); // Labels ctx.font = '18px Arial'; ctx.fillStyle = '#333'; ctx.textAlign = 'center'; ctx.fillText('solid thick', 300, 60); ctx.fillText('thick + round caps', 300, 120); ctx.fillText('dashed', 300, 200); ctx.fillText('dotted + round', 300, 260); </script> |
Key lessons from this example:
- lineCap = how line ends look
- ‘butt’ → flat cut (default)
- ’round’ → half-circle (best for dots)
- ‘square’ → square cap
- setLineDash([dashLength, gapLength]) → creates dashed/dotted lines Common patterns:
- [10, 5] → classic dashed
- [2, 8] → dotted
- [20, 10, 5, 10] → dash-dot pattern
- Always reset with ctx.setLineDash([]) after using dashes — otherwise next lines become dashed too!
Your three tiny practice tasks (10–15 minutes each)
Task 1 – Four lines showcase Draw four horizontal lines across the canvas:
- thin solid black at y=80
- thick blue with round caps at y=160
- medium green dashed at y=240
- orange dotted with round caps at y=320
Task 2 – Coordinate axes Draw a gray cross through the center of the canvas
- label the center “(300, 200)” with text
Task 3 – Zigzag arrow Start at left side → zigzag 5–6 times → end with an arrowhead (Use thick line + round caps + lineJoin=’round’)
Paste any of them here when you finish — I’ll review it like we’re pair-programming together.
Which part still feels a little confusing?
- Why do we need beginPath() every single time?
- Difference between stroke() and fill()?
- How lineCap actually changes the look?
- Making nice dashed/dotted lines?
- Something else?
Tell me — we’ll stay on fill/stroke/lines until you feel super confident drawing anything with them.
You’re doing really well — this is the foundation of 90% of Canvas drawings! 🚀
