In our previous canvas tag tutorial we saw how to have access to canvas context.
Once you have access to it you can start drawing.

Overview

Once you prepared the context you can actually instruct it to draw.

You can draw complex shapes using "paths". A path is made out of draw primitives.

To draw a path you have to take the following steps:

  1. beginPath()
  2. add lines, curves, arcs, etc
  3. (optionally) closePath()
  4. stroke() or fill()

beginPath()

Any new path must start with a beginPath() call on canvas context.

beginPath() function instruct the canvas to start a new drawing session. This will actually erase previous path (if any) and creates a new one.

//begin a new path
ctx.beginPath();
Every time you call it, is like you place your pen above the canvas and you are about to write.

Also initially the pen is not positioned anywhere....is "in the air"

moveTo()

After you opened a new path you have to position your pen on canvas.

As initially the pen is still "in the air", you must place it somewhere on the canvas.

To do that you must use moveTo() method which will position the pen at the desired position.

//position the pen at (0,0) coordinates
ctx.moveTo(0, 0);
Every time you call it is like you place your pen on the canvas and you are about to continue the drawing.

If do not do that the first draw instruction will be ignored and used to position the pen.

Add lines, curves, arcs, etc

Once you have the path started you can:
  • add lines
  • add curves
  • add arcs
  • and other primitives
Here is how to draw some primitives:

//draw a line to (100, 100)
ctx.lineTo(100, 100);

//draw  (quadratic) curve from last point (100, 100) to (400, 100)
ctx.bezierCurveTo(150, 50, 300, 150, 400, 100)
Every time you draw a primitive: line, curve, circle the pen will stop where the last drawn primitive stopped.

So, in our case, the curve will start from where the line ended.

closePath()

After you added the primitives you wanted you must tell context to close the path. This means that the method will try to draw a line from last painted point in path to the initial point in path.

//close current path
ctx.closePath();

If the path was already closed or the path is a single point than this method is ignored.

A special case appear when you call fill() - it will simply try to visually close the path automatically and fill it. So people think that fill() will close the path when it does not....it will do it only visually...see the real examples on fill();

Any new primitive (line, curve, etc) added after will not be added to the path.

stroke()

Stroke simply outline currently opened path, up to the last point.

//outline current path
ctx.stroke();

You can continue to add new primitives to current path and call stroke() again.

Anyway, there is something bizarre about stroke and that is that stroke() will repaint the same path up the current point from the beginning, even if previous parts of the path were painted before. This will result in the outline of the path to become thicker and darker as if you use the pen to thicker a line.

Here is what I'm trying to tell (use Firefox to see it better).

A single paint

Triple paint (observe thicker outline)

fill()

fill() simply fill current path with current fill color.

//fill path
ctx.fill();

Bellow you can see some special cases of using the fill() in conbination with stroke();

"default" close path of fill(). As you can see the triangle path is filled but does not close the shape which can be outlined by subsequent stroke() calls.

 

Real call to closePath(). Here, after we filled the triangle we close the path and then call stroke. As you can see the closePath() simply draw a line from current point to the initial point (initial visible point).

 

See that the the path uses first visible point in path when closing it with closePath(); it uses (30, 30) instead of (10, 10)

Resources

Advanced path painting a deep study of 2d context of canvas.