HTML5 Canvas: Paths

Jakob Jenkov
Last update: 2014-06-15

A HTML5 Canvas path is a series of points with drawing instructions between those points. For instance, a series of points with lines between them, or with arcs between them.

Paths are used to draw many types of shapes (lines, circles, polygons etc.) on an HTML5 canvas, so it is important to understand this central concept.

Beginning and Closing a Path

A path is started and ended using the 2D Context functions beginPath() and closePath(), like this:

var canvas  = document.getElementById("ex1");
var context = canvas.getContext("2d");

context.beginPath();

//... path drawing operations

context.closePath();

moveTo()

When drawing using a path, you are using a virtual "pen" or "pointer". This virtual pointer is always located at some point. Moving the virtual pointer is done using the 2D Context function moveTo(x, y), like this:

context.moveTo(10,10);

This example moves the pointer to the point 10, 10.

lineTo()

The lineTo() function draws a line from the location of the virtual pointer to the point passed as parameter to the lineTo() function. Here is an example:

context.beginPath();

context.moveTo(10,10);
context.lineTo(50,50);

context.closePath();

This example moves the pointer to the point 10,10 and then draws a line from that point to the point 50, 50.

lineTo() also moves the virtual pointer to the end point of the line. Thus, in the example above the pointer is moved to 50,50 , while instructing the canvas to draw a line to that point.

stroke() + fill()

Nothing of a path is actually drawn until you instruct the 2D Context to draw the path. This is done by calling either stroke() or fill() on the 2D Context.

The stroke() function will draw the outline of the shape defined by the path operations.

The fill() function will fill out the shape defined by the path operations.

Here are an example of both stroke() and fill() applied to identical shapes:

context.beginPath();
context.moveTo(10,10);
context.lineTo(60,50);
context.lineTo(110,50);
context.lineTo(10,10);
context.stroke();
context.closePath();

context.beginPath();
context.moveTo(100,10);
context.lineTo(150,50);
context.lineTo(200,50);
context.lineTo(100,10);
context.fill();
context.closePath();

Here is the result of the above code:

HTML5 Canvas not supported

Line Width

You can set the width of the line drawn by the various stroke functions using the lineWidth property of the 2D Context. Here is an example:

context.lineWidth = 10;

The example above sets the line width to 10 pixels for all subsequent stroke drawing operations.

Here are three lines drawn with a line width of 1, 5 and 10:

HTML5 Canvas not supported

With line widths larger than 1 the extra width of the line is drawn outside the center line. That is, if you draw a line from 10,10 to 100,10 with a line width of 10, the line will actually start already at 10,5 and extend through 10,15, and horizontally to 100,5 and 100,15 from there. Like a rectangle.

Line Cap

When drawing lines using a path you can set the lines line cap. The line cap defines how the end of the line is drawn.

The line cap is set via the lineCap property of the 2D Context. It can take these values:

  • butt
  • round
  • square

The value butt results in a line end that is flat and orthogonal to the line.

The value round results in a rounded line end, with a radius of the rounding that is equal to half the width of the line.

The value square results in a rectangle being drawn at the end of the line. The rectangle has the size of line width x line width / 2.

Here are a set of examples illustrating the line caps. All lines are drawn with a line width of 10. The left most lines uses the lineCap value butt. The middle lines uses the lineCap value round. The rightmost lines uses the lineCap value square.


HTML5 Canvas not supported

It can be a bit difficult to see the difference between lines drawn with a lineCap value of butt and square. Therefore I have created a few examples of line pairs using butt and square, drawn close to each other so you can see the difference. The top or left line is using butt, and the bottom or right line is using square.


HTML5 Canvas not supported

As you can see, the lines using a lineCap value of square have an extra rectangle drawn at the end, which makes the line a little longer.

Line Join

The lineJoin property of the 2D Context defines how the point is drawn where two lines are connected. The point where two lines are connected is called the "line join". The lineJoin property can have the following values:

  • miter
  • bevel
  • round

Here are three code examples setting the line join:

context.lineJoin = "miter";
context.lineJoin = "bevel";
context.lineJoin = "round";

A value of miter results in a triangular corner being drawn for the line join.

A value of bevel results in a flat (linear) corner being drawn for the line join.

A value of round results in a round corner being drawn for the line join.

Here are three examples showing (from the left) miter, bevel and round used as value for the lineJoin property.


HTML5 Canvas not supported

arc()

The 2D Context function arc() draws an arc on the canvas.

The arc() function takes 6 parameters:

  • x: x coordinate of center point of arc.
  • y: y coordinate of center point of arc.
  • radius: radius of arc.
  • startAngle: angle in radians from which the arc starts.
  • endAngle: angle in radians at which the arc ends.
  • anticlockwise: sets whether the direction to draw in is anticlockwise or not (not = clockwise).

Here is a code example:

context.lineWidth = 3;

var x = 50;
var y = 50;
var radius = 25;
var startAngle = (Math.PI / 180) * 45;
var endAngle   = (Math.PI / 180) * 90;
var anticlockwise = false;

context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, anticlockwise);
context.stroke();
context.closePath();

This code example draws an arc with center point at 50, 50 with a radius of 25 pixels, starting from 45 degrees and continuing until 180 degrees. The degrees going from 0 to 360 are converted to radians, as you may have noticed.

Here is how the code example looks when drawn on a canvas:


HTML5 Canvas not supported

Here is the same code sample, but with anticlockwise set to true:


HTML5 Canvas not supported

To draw a full circle, simple set startAngle to 0 and endAngle to
2 * Math.PI which is equal to (Math.PI / 180) * 360.

arcTo()

The 2D Context has an arcTo() function, but its abilities can be mimicked using lineTo() and arc(), so I will skip over it.

quadraticCurveTo()

The quadraticCurveTo() function draws a quadratic Bezier curve from one point to another. The curve is controlled by a single control point. Here is a code example:

context.lineWidth = 3;

var fromX = 50;
var fromY = 50;
var toX   = 100;
var toY   = 50;
var cpX   = 75;
var cpY   = 100;

context.beginPath();
context.moveTo(fromX, fromY);
context.quadraticCurveTo(cpX, cpY, toX, toY);
context.stroke();
context.closePath();

This code example draws a curve from 50, 50 to 100, 50 using the control point 75, 100 (cpX, cpY). The resulting curve looks like this:


HTML5 Canvas not supported

The little dot on the canvas is the control point which I have drawn there. It is not part of the curve normally.

bezierCurveTo()

The bezierCurveTo() function draws a cubic Bezier curve from one point another. A cubic Bezier curve has 2 control points, whereas the quadratic Bezier curve only has 1 control point. Here is a code example:

context.lineWidth = 3;

var fromX = 50;
var fromY = 50;
var toX   = 300;
var toY   = 50;
var cp1X   = 100;
var cp1Y   = 100;
var cp2X   = 250;
var cp2Y   = 100;

context.beginPath();
context.moveTo(fromX, fromY);
context.bezierCurveTo(cp1X, cp1Y, cp2X, cp2Y, toX, toY);
context.stroke();
context.closePath();

This code example draws a curve from 50, 50 to 300, 50 using the control points 100, 100 (cp1X, cp1Y) and 250, 100 (cp2X, cp2Y). The resulting curve looks like this:


HTML5 Canvas not supported

The two little dots on the canvas are the control points that I have drawn to show you where they are. They are not drawn as part of the curve.

Here is a different example using different start point, end point and control point:

context.lineWidth = 3;

var fromX = 50;
var fromY = 50;
var toX   = 300;
var toY   = 50;
var cp1X   = 100;
var cp1Y   = 10;
var cp2X   = 250;
var cp2Y   = 100;

context.beginPath();
context.moveTo(fromX, fromY);
context.bezierCurveTo(cp1X, cp1Y, cp2X, cp2Y, toX, toY);
context.stroke();
context.closePath();

Here is the corresponding curve:


HTML5 Canvas not supported

Again, the two little dots are the control points that I have rendered explicitly.

Jakob Jenkov

Featured Videos

Java ConcurrentMap + ConcurrentHashMap

Java Generics

Java ForkJoinPool

P2P Networks Introduction

















Close TOC
All Tutorial Trails
All Trails
Table of contents (TOC) for this tutorial trail
Trail TOC
Table of contents (TOC) for this tutorial
Page TOC
Previous tutorial in this tutorial trail
Previous
Next tutorial in this tutorial trail
Next