Page 16: Pens and Paths
Spring 2026 Sample Solution
On the previous page, we learned about Canvas, but we only drew rectangles. In fact, the rectangles were all axis-aligned (they are parallel with the coordinate axes).
Canvas has 2 kinds of shapes: rectangles, and everything else. It also allows drawing text and images. But most everything else is drawn as the “everything else” mechanism: via the path and pen metaphor. Other APIs, such as SVG, use the path and pen metaphor as well.
Pens and paths are discussed in the lecture videos, but I’m not sure they add much beyond what is discussed here.
Part of 2023 Lecture 3 | Slides
Paths
To make a path based shape, you define a “path” - which is basically the outline of the shape, and then you can stroke and fill that.
You can think of the path as a trace of a pen. Your program gives a sequence of commands that tell the pen where to go. This builds an internal datastructure that describes the path. Once you’ve built up the data structure, you can use commands to make the path visible by “stroking” along it or “filling” inside it.
The most basic path commands are moveTo and lineTo. moveTo moves the pen to a new location, while lineTo connect from the last point to the new one.
|
|
A few things to notice…
- To make the example more concise, I removed the lines that get the canvas element and then get the context from it. There is a line that says
const ctx = canvas.getContext('2d');. - The first line moves to the point 50,50 (near the top left). The next
lineToextend from that point. - Line 4 again uses
moveToto move the pen (without creating a line). The nextlineTomove on from there. - Paths do not have to be connected.
We always extend onto the current path. The stroke and fill functions operate on the current path using the current drawing state. They do not change the state, so we can draw a path twice.
|
|
Notice that the first stroke (on line 5) draws the current points (the first three), with the current “pen” (green, thickness 5). The next three points are then added, so that the path has 6 points by the time it gets to line 9. Line 10 draws the path through all six points, in the thin black style set on line 9.
If we want to start a new path, we need to use the beginPath function. Compare this next example with the previous one:
|
|
Here, line 7 starts a new path, so line 11 only considers the path with points after that.
Paths are not “closed.” If we want to connect the last point to the first, we can use closePath() which connects to most recent moveTo, not the beginning of the path.
|
|
The closePath on line 9 is equivalent to doing a lineTo to the most recent moveTo point (100,50). Using closePath means that we don’t need to remember the position. It also is exact (in case there are potential for computation errors).
Not Straight
There are other commands than extend the path. To draw circles, or pieces of circles, there are two different commands for drawing arcs: arc and arcTo. Both commands extend the path, but they take different parameters.
|
|
The simpler one is arc: it takes the center and radius of the circle, the beginning and ending angle around the circle, and whether to go clockwise or counter-clockwise. On line 3, the start of the circle is at (-90 degrees, measured from horizontal), so it connects to the end of the line at 100,100.
This might be clearer if I draw the entire circle as well…
|
|
Some things to notice here:
- Before setting the thick gray for the circle, I used
saveso I couldrestore - Line 4 creates the path for the whole circle, from 0 around to
- Because the circle is at the beginning of a path (it’s a new canvas), I didn’t need to worry about that point connecting to some other path
- I restore the initial drawing state and reset the path before drawing the object that I drew before.
- Hopefully it is now more clear that the 45 degree line segment is drawn between the point on line 9 and the beginning of line 10
The point about the beginning of the path (#3) is important. Observe:
|
|
We’ve connected from the “end” of one circle to the beginning of the other.
The other form of specifying arcs is arcTo. The parameters are the points where the arc begins and ends, and the function fits the arc to it.
There are also path commands that extend the path with polynomial curve segments. We’ll explain these later in the semester when we discuss curves.
Filling
Just as the rectangle had fillRect and strokeRect paths have stroke and fill. The biggest difference is that rather than telling stroke and fill what the shape is, it uses the current path. That is, the current path is part of the context, the same way things like fill color and line width are.
|
|
|
|
Fill acts as if a closePath was done (but stroke does not).
The order of fill and stroke matters: fill will cover over part of your stroke.
|
|
|
|
When paths get complicated, we need rules to help us define what is inside and outside the shape for filling. We’ll discuss that Next: Page 17 - Polygons: Inside and Out.