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.
All we need to start is an HTML page with a canvas tag and a JavaScript file to manipulate it with.
<!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.
// 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
})
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 before stroke
or fill
.stroke
: Renders an outline of everything before it.fill
: Renders the whole shape as a solid color.strokeStyle
and fillStyle
: Sets the outline and shape color. They are not functions like the others and need to be assigned a string.strokeRect
and fillRect
: Same as stroke
and fill
but only for that item, works the same as rect
.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.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)
beginPath
: Starts a new Linestroke
: Renders the linemoveTo(x-axis, y-axis)
: Sets the starting pointlineTo(x-axis, y-axis)
: Renders a line from the previous endpointlineWidth
: Set the line’s thicknessAnd 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()
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))
c.lineWidth = 5
c.beginPath()
c.arc(400, 400, 50, 0, Math.PI * 2)
c.stroke()
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 here. Bezier curves, on the other hand, have two control points, at each end of the curve for more complex shapes. Another great example here.
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:
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 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
, and center
.c.font = '60px Times-New-Roman'
c.fillText("Hello World", 600, 500)
c.strokeText('Hello World', 1200, 500)
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.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!