HTML5 Canvas API Tutorial Part 4 - Drawing Text

Published: Thursday, January 21, 2021
Updated: Monday, February 1, 2021

Greetings, friends! Welcome to Part 4 of my HTML5 Canvas API tutorial. In this tutorial, I will discuss how to draw text to an HTML5 canvas.

Canvas Setup

Create a file called index.html with the following contents:

html
Copied! ⭐️
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Canvas API Tutorial</title>
</head>
<body>
  <h1>Canvas API Tutorial</h1>
  <canvas id="canvas" width="500" height="500" style="border: 1px solid black;"></canvas>
  <script src="canvas.js"></script>
</body>
</html>

Create a script called canvas.js with the following contents in the same directory:

js
Copied! ⭐️
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

Drawing Text on the Canvas

To draw text to the canvas, we will utilize these two methods frequently: ctx.fillText and ctx.strokeText. Each method takes four arguments:

  • text: A DOMString specifying the text string to render into the context. The text is rendered using the settings specified by font, textAlign, textBaseline, and direction.
  • x: The x-axis coordinate of the point at which to begin drawing the text, in pixels.
  • y: The y-axis coordinate of the point at which to begin drawing the text, in pixels.
  • maxWidth (optional): The maximum number of pixels wide the text may be once rendered.

Let's try it out! We will add the following line to the canvas.js file:

js
Copied! ⭐️
ctx.fillText('Hello world', 200, 200)

This will draw the text, "Hello World", to the canvas at the position, (200, 200) where the bottom-left corner of the beginning of text is roughly at (200, 200).

The "font" Property

You will notice immediately that the font seems a bit too small. The default font is 10px, sans-serif. Let's change the font size by setting the ctx.font property:

js
Copied! ⭐️
ctx.font = '48px serif'
ctx.fillText('Hello world', 200, 200)

Rendering text to the HTML Canvas

Much better! By changing the font property on the canvas context before we call ctx.fillText or ctx.strokeText, we can change certain properties of the font such as font size, font weight, and font family.

When setting the value of the ctx.font property, we pass in a string that is parsed as a CSS font value. It's very similar to setting the font property in a CSS file. For example, let's set the font-weight to bold and font-family to cursive:

js
Copied! ⭐️
ctx.font = 'bold 48px cursive'
ctx.fillText('Hello world', 200, 200)

Rendering text with bold font to the HTML Canvas

If you look at the ctx.font MDN article, you may see it mention something known as a DOMString. The MDN article associated with a DOMString might sound confusing, but you shouldn't get caught up in the details. A normal JavaScript string is automatically a DOMString instance. Therefore, the ctx.font property simply accepts a normal string that is then parsed as a CSS font property. DOMStrings are kept around for legacy reasons to ensure interoperability between certain Web APIs.

The "textAlign" Property

You can align text horizontally using the ctx.textAlign property. You can choose between the following: "left", "right", "center", "start", "end".

Let's look at an example of each type of text alignment. We'll replace the contents of canvas.js with the following:

js
Copied! ⭐️
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const canvasCenter = canvas.width / 2

ctx.beginPath()
ctx.moveTo(canvasCenter, 0)
ctx.lineTo(canvasCenter, canvas.height)
ctx.stroke()

ctx.font = '30px serif'

ctx.textAlign = 'left'
ctx.fillText('left-aligned', canvasCenter, 40)

ctx.textAlign = 'center'
ctx.fillText('center-aligned', canvasCenter, 85)

ctx.textAlign = 'right'
ctx.fillText('right-aligned', canvasCenter, 130)

ctx.textAlign = 'start'
ctx.fillText('start-aligned-ltr', canvasCenter, 175)

ctx.textAlign = 'end'
ctx.fillText('end-aligned-ltr', canvasCenter, 220)

ctx.direction = 'rtl'

ctx.textAlign = 'start'
ctx.fillText('start-aligned-rtl!', canvasCenter, 265)

ctx.textAlign = 'end'
ctx.fillText('end-aligned-rtl!', canvasCenter, 310)

This will let us see how the text looks under different alignments. If you choose not set the ctx.textAlign property, it will be set to "start" by default.

Rendering text on the canvas with different textAlign values

warning
Notice that I have changed the direction of the text using the ctx.direction. As stated on MDN, this feature is in an experimental phase, which means not all browsers support it yet.

When the text is going from right to left (rtl), certain characters will appear at the beginning of the text. Some languages such as Persian read from right to left and therefore the punctuation (such as an exclamation mark or question mark) is placed at the far-left of the text.

The "textBaseline" Property

Sometimes, you need to adjust the text baseline depending on the language you're using or how you want to align text with other elements on the canvas. This can be done through the ctx.textBaseline property. You can set this property to one of the following values: "top", "hanging", "middle", "alphabetic", "ideographic", "bottom".

The following image from the WHATWG website demonstrates different alignment points in the font. Each alignment point corresponds to a value that can be set on the ctx.textBaseline property.

Diagram displaying various baselines supported by the textBaseline property

Let's see an example of each baseline attribute, taken directly from MDN's website:

js
Copied! ⭐️
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

const baselines = ['top', 'hanging', 'middle', 'alphabetic', 'ideographic', 'bottom']
ctx.font = '36px serif'
ctx.strokeStyle = 'red'

baselines.forEach((baseline, index) => {
  ctx.textBaseline = baseline
  const y = 75 + index * 75
  ctx.beginPath()
  ctx.moveTo(0, y + 0.5)
  ctx.lineTo(550, y + 0.5)
  ctx.stroke()
  ctx.fillText(`Abcdefghijklmnop (${baseline})`, 0, y)
})

This will result in six lines that are equidistant from each other. Notice how the text is rendered differently depending on the baseline.

Rendering text on the canvas with different textBaseline values

Gathering Text Metrics

You may want to gather certain dimensions of the text rendered to the canvas such as the width, measured in CSS pixels. We can retrieve the width of the text from the TextMetrics object we get back from the ctx.measureText method. Things like the font-size, font-weight, and font-family can affect the value of the width.

Let's look at an example. Replace all the contents of canvas.js with the following:

js
Copied! ⭐️
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

const smallText = ctx.measureText('Hello world')
console.log(smallText.width)
ctx.fillText('Hello world', 100, 100)

ctx.font = '48px serif'
const bigText = ctx.measureText('Hello world')
console.log(bigText.width)
ctx.fillText('Hello world', 100, 200)

The small text is 10px, sans-serif by default. When I measure the width using Google Chrome, I get a width of 49.462890625 pixels for the smallText and a width of 230.625 pixels for the bigText. These values may change depending on your browser, device, font size, font weight, font family, and other properties that affect the font.

You might be wondering why we should care about the width of the text if it changes depending on the browser and device. Wouldn't that make it hard to develop applications that work across different browsers then? When developing applications, we can use the width of the text and compare it against something such as the size of a rectangle on the canvas or the entire canvas width. By looking at the text width as it relates to other objects, we can make it more useful.

Rendering text inside a canvas can be difficult. You will commonly see developers use normal HTML elements for rendering text alongside the HTML canvas. Sometimes, you need to use tools the Canvas API provides for manipulating text. Choose the tool that makes more sense for the application you're trying to build.

Conclusion

The HTML5 Canvas API provides two methods for rendering text to the canvas: ctx.fillText and ctx.strokeText. There are four main properties you can adjust for styling text: ctx.font, ctx.textAlign, and ctx.textBaseline. You can control the ctx.font property like the CSS font property. Additionally, you can use the ctx.measureText method to retrieve metadata about the text you want to render to the screen. I should note that there are some experimental APIs that add more text rendering features such as ctx.direction, but they aren't supported in every browser yet.

In the next tutorial, I'll dive into how to insert images into the canvas. Then, we can move onto pixel manipulation using the Canvas API! Exciting times ahead so don't miss it! 😃

Resources