Jess

Posted on

# Getting Started with the Canvas API: Complex Shapes

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

``````

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.

Jess

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!

yellow1912

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.