Table of Contents
URL: https://www.progressiverobot.com/js-drawing-shapes-canvas-api/
In this article we'll be looking at the HTML canvas element and the JavaScript canvas API to render complex shapes onto our web pages.
Setup
All we need to start is an HTML page with a canvas tag and a JavaScript file to manipulate it with.
[label index.html]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="ie=edge"/>
<title>HTML Canvas</title>
</head>
<body>
<canvas></canvas>
</body>
<script src="./canvas.js"></script>
</html>
With our canvas element in place, we now need to create a new variable with it and a canvas context, which adds a bunch of functionality onto our canvas. To keep things simple we'll stick with 2D shapes, but with the webgl context, 3D is also possible.
For our example we'll need our canvas to be fullscreen but setting the size using CSS creates a strange blurry effect, which we obviously don't want, so we'll have to set it here.
[label canvas.js]
// getting a reference to our HTML element
const canvas = document.querySelector('canvas')
// initiating 2D context on it
const c = canvas.getContext('2d')
addEventListener('resize', () => {
canvas.width = innerWidth
canvas.height = innerHeight
})
Rectangles
To draw rectangles, on our context variable (c), we can start adding what we want, measured in pixels:
rect(x-axis, y-axis, width, height): Sets the location and dimensions of our rectangle, and needs to be called beforestrokeorfill.
stroke: Renders an outline of everything before it.
fill: Renders the whole shape as a solid color.
strokeStyleandfillStyle: Sets the outline and shape color. They are not functions like the others and need to be assigned a string.
strokeRectandfillRect: Same asstrokeandfillbut only for that item, works the same asrect.
clearRect(x-axis, y-axis, width, height): Clears everything inside of a certain area. Very useful when we get into animations where we're constantly rendering new elements and don't want the old ones to stick around.
[label canvas.js]
c.strokeStyle = 'white'
c.fillStyle = 'blue'
c.rect(100, 20, 150, 100)
c.stroke()
c.fill()
c.fillStyle = 'red'
c.fillRect(400, 500, 300, 250)
// Uncomment to remove the first two blocks
// c.clearRect(0, 0, canvas.width, canvas.height)
c.fillStyle = 'green'
c.fillRect(1500, 500, 300, 250)
Lines
beginPath: Starts a new Line
stroke: Renders the line
moveTo(x-axis, y-axis): Sets the starting point
lineTo(x-axis, y-axis): Renders a line from the previous endpoint
lineWidth: Set the line's thickness
And here are a few examples where we draw some lines:
// Just a basic line
c.beginPath()
c.moveTo(40, 250)
c.lineTo(200, 500)
c.strokeStyle = 'red'
c.stroke()
// Draw the letter M
c.beginPath()
c.moveTo(1500, 700)
c.lineTo(1600, 450)
c.lineTo(1700, 700)
c.lineTo(1800, 450)
c.lineTo(1900, 700)
c.strokeStyle = 'blue'
c.stroke()
// Let's now draw a house
c.lineWidth = 10
c.strokeStyle = 'red'
c.fillStyle = 'red'
// Walls
c.strokeRect(800, 500, 300, 200)
// Door
c.fillRect(925, 600, 50, 100)
// Roof
c.beginPath()
c.moveTo(700, 500)
c.lineTo(1200, 500)
c.lineTo(950, 300)
c.lineTo(700, 500)
c.stroke()
Circles
The only method we really need for drawing circles is arc. The angles are taken in radians and not degrees so for our end-angle we can just use Math.PI * 2, since that's equal to 360 degrees, and the starting angle can be left at 0. We're not going to need to specify a value for counterclockwise, so we can just leave it off since it defaults to false.
arc(x, y, radius, starting-angle, end-angle, counterclockwise (boolean))
[label canvas.js]
c.lineWidth = 5
c.beginPath()
c.arc(400, 400, 50, 0, Math.PI * 2)
c.stroke()
Quadratic and Bezier Curves
If you've ever used graphic design tools like Photoshop or Affinity Designer, these will seem very similar to some of their line tools.
Essentially, quadratic and bezier curves are just free form lines with different methods of control. Quadratic curves are simpler in that they just have a start, endpoint, and what's known as the control point, which acts as a handle for curving the line. You can see a wonderful interactive example blogs.sitepointstatic.com. Bezier curves, on the other hand, have two control points, at each end of the curve for more complex shapes. Another great example blogs.sitepointstatic.com.
quadraticCurveTo(controlPoint-x, controlPoint-y, endpoint-x, endpoint-y)
bezierCurveTo(startControlPoint-x, startControlPoint-y, endControlPoint-x, endControlPoint-y, endpoint-x, endpoint-y)
And some examples:
[label canvas.js]
c.lineWidth = 5
c.strokeStyle = 'white'
c.beginPath()
c.moveTo(400, 400)
c.lineTo(400, 300)
c.quadraticCurveTo(450, 250, 500, 300)
c.lineTo(500, 400)
c.stroke()
c.beginPath()
c.moveTo(800, 400);
c.bezierCurveTo(800, 150, 1200, 700, 1200, 400);
c.stroke()
Text
Text works very similarly to rectangles with a few CSS-like options for styling:
fillText(text, x, y)
strokeText(text, x, y)
font:Takes a string with the size in pixels and font family; like '60px Times-New-Roman'.
textAlign: Takes a string with the same options as its CSS counterpart;start,end,left,right, andcenter.
[label canvas.js]
c.font = '60px Times-New-Roman'
c.fillText("Hello World", 600, 500)
c.strokeText('Hello World', 1200, 500)
Conclusion
While there is still an enormous amount that can be done with HTML canvas like animations and interactivity, hopefully this was a good first introduction to some of its possibilities.