HTML5 Canvas: Paths
Jakob Jenkov |
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:
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:
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
.
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
.
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.
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:
Here is the same code sample, but with anticlockwise
set to true
:
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:
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:
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:
Again, the two little dots are the control points that I have rendered explicitly.
Tweet | |
Jakob Jenkov |