In Part 1 of Getting Started with the Canvas API I went over the basics of creating lines and rectangles. Here I'm going to discuss creating more complex shapes.
First, I'm going to create a 300x300 canvas element and get a reference to it in JavaScript.
<canvas id="canvas" height="300" width="300"></canvas>
const ctx = document.getElementById('canvas').getContext('2d');
Connecting Lines
As I explained in Part I, you can create lines by beginning a path (using beginPath()
), plotting the x,y values of the beginning and end of your line using the moveTo()
and lineTo()
methods, and creating the line stroke()
. You can actually continue plotting points for your line before creating the stroke.
When lineTo()
is used, it creates an x,y end point for the line. When it is used again along the same path, the line extends from the previous end point.
ctx.lineWidth = 3; // just making it a little more visible
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(20,100);
ctx.lineTo(100,100);
ctx.lineTo(20,200);
ctx.lineTo(100,200);
ctx.lineTo(200, 20);
ctx.lineTo(200, 200);
ctx.stroke();
Triangles
In order to create a closed shape, you can create a lineTo()
back to the starting point.
ctx.lineWidth = 6;
ctx.beginPath();
ctx.moveTo(ctx.canvas.width/2, 20);
ctx.lineTo(20, ctx.canvas.height - 20);
ctx.lineTo(ctx.canvas.width - 20, ctx.canvas.height - 20);
ctx.lineTo(ctx.canvas.width/2,20);
ctx.stroke();
But ew, what's going on here?
You can fix this by changing the fillCap
of your line, but then it doesn't quite match the other pivot points.
ctx.fillCap = 'round';
Instead of changing the fillCap
, here's a better option:
Use closePath()
in place of the final lineTo()
point connection and it will automatically connect those points neatly.
ctx.lineWidth = 6;
ctx.beginPath();
ctx.moveTo(ctx.canvas.width/2, 20);
ctx.lineTo(20, ctx.canvas.height - 20);
ctx.lineTo(ctx.canvas.width - 20, ctx.canvas.height - 20);
ctx.closePath(); // <--- replaced the final lineTo with closePath
ctx.stroke();
Line Joins
There are also lineJoin
options to style your segment connections: bevel
, miter
, and round
. round
rounds off the corners, miter
joins the outside edges of the line segments to a single point and is the default, and bevel
fills the endpoint of the connected line segments and flattens it off.
Filling Shapes
You can fill in the shape by using fillStyle
and fill()
.
ctx.fillStyle = 'red'; < --- set the color
ctx.lineWidth = 6;
ctx.beginPath();
ctx.moveTo(ctx.canvas.width/2, 20);
ctx.lineTo(20, ctx.canvas.height - 20);
ctx.lineTo(ctx.canvas.width - 20, ctx.canvas.height - 20);
ctx.fill(); < --- fill the shape
ctx.closePath();
ctx.stroke();
Order matters here. If you fill()
after stroke()
, the outline will appear thinner because the fill is on top.
ctx.fillStyle = 'red';
ctx.lineWidth = 6;
ctx.beginPath();
ctx.moveTo(ctx.canvas.width/2, 20);
ctx.lineTo(20, ctx.canvas.height - 20);
ctx.lineTo(ctx.canvas.width - 20, ctx.canvas.height - 20);
ctx.closePath();
ctx.stroke();
ctx.fill(); < --- fill the shape after creating the stroke
Top comments (3)
Thank you. In the next part can you show how to create soft curve lines that connect objects (as we often see in graphs like this intercom.com/series?on_pageview_ev...
I actually want to create something similar but not sure where to start.
You mean like how a line connects to a circle or arrowhead? My next post will probably be on circles and curved lines so hopefully it'll be what you're looking for. I'll see if I can whip up an example that will help you get started!
Thank you very much. I'm interested in those curved lines and how we should support redrawing those lines kinda dynamically when we drag elements around.