Part of Series: HTML Canvas
HTML

How to wrap text in HTML Canvas

Sponsor

Although adding text to HTML canvas is very common, there is no built in line break functionality. That means if our text is too long, the text will run off the end. Take the example below, where the text is supposed to be "Hello, this text line is very long. It will overflow". Since it's too long to fit in the canvas, it just overflows with no line breaks:

Code for this example:

let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d'); let grd = ctx.createLinearGradient(0, 853, 1352, 0); grd.addColorStop(0, '#00a0ff'); grd.addColorStop(1, '#12cba6'); ctx.fillStyle = grd; ctx.fillRect(0, 0, 1342, 853); // More text ctx.font = '700 95px Helvetica'; ctx.fillStyle = 'white'; ctx.fillText("Hello, this text line is very long. It will overflow.", 85, 200);

Our text above starts at (85, 200)px, and continues with no line breaks. As odd as it is, we need to calculate ourselves where the line breaks should be in HTML Canvas. To do that, we can use a custom function, and use the data from that function to put line breaks in place.

How to wrap text in HTML Canvas

When we build our custom function to wrap text in HTML, we need to consider when a line break occurs. A line break typically occurs when the next word is going to overflow the width of the parent element - in this case, our canvas. When we build our function to wrap the text, we need to check if the next word in the sentence will cause an overflow.

As such, we'll build a function which accepts a few different variables:

  • ctx - the context for the canvas we want to wrap text on.
  • text - the text we want to wrap.
  • x - the X starting point of the text on the canvas.
  • y - the Y starting point of the text on the canvas.
  • maxWidth - the width at which we want line breaks to begin - i.e. the maximum width of the canvas.
  • lineHeight - the height of each line, so we can space them below each other.

Let's take a look at the function I've built for this:

// @description: wrapText wraps HTML canvas text onto a canvas of fixed width // @param ctx - the context for the canvas we want to wrap text on // @param text - the text we want to wrap. // @param x - the X starting point of the text on the canvas. // @param y - the Y starting point of the text on the canvas. // @param maxWidth - the width at which we want line breaks to begin - i.e. the maximum width of the canvas. // @param lineHeight - the height of each line, so we can space them below each other. // @returns an array of [ lineText, x, y ] for all lines const wrapText = function(ctx, text, x, y, maxWidth, lineHeight) { // First, start by splitting all of our text into words, but splitting it into an array split by spaces let words = text.split(' '); let line = ''; // This will store the text of the current line let testLine = ''; // This will store the text when we add a word, to test if it's too long let lineArray = []; // This is an array of lines, which the function will return // Lets iterate over each word for(var n = 0; n < words.length; n++) { // Create a test line, and measure it.. testLine += `${words[n]} `; let metrics = ctx.measureText(testLine); let testWidth = metrics.width; // If the width of this test line is more than the max width if (testWidth > maxWidth && n > 0) { // Then the line is finished, push the current line into "lineArray" lineArray.push([line, x, y]); // Increase the line height, so a new line is started y += lineHeight; // Update line and test line to use this word as the first word on the next line line = `${words[n]} `; testLine = `${words[n]} `; } else { // If the test line is still less than the max width, then add the word to the current line line += `${words[n]} `; } // If we never reach the full max width, then there is only one line.. so push it into the lineArray so we return something if(n === words.length - 1) { lineArray.push([line, x, y]); } } // Return the line array return lineArray; }

This function works on a few premises:

  • We test a new line by using measureText(). If it's too long for the container, then we start a new line. Otherwise we stay on the current one.
  • We use a predefined line height, so that we can have consistent line heights.
  • We return an array of [ lineText, x, y ] for each line - where lineText is the text for that line, and x/y is the starting position of that particular line.
  • If there is only one line, we just return that line in lineArray.

To apply it to our canvas, we have to iterate over each element from the array. Then we use ctx.fillText to draw each line at the coordinates calculated by our wrapText() function - which will ultimately create line breaks for us:

// Set up our font and fill style ctx.font = '700 95px Helvetica'; ctx.fillStyle = 'white'; // we pass in ctx, text, x, y, maxWidth, lineHeight to wrapText() // I am using a slightly smaller maxWidth than the canvas width, since I wanted to add padding to either side of the canvas. let wrappedText = wrapText(ctx, "This line is way too long. It's going to overflow - but it should line break.", 85, 200, 1050, 140); // wrappedTe wrappedText.forEach(function(item) { // item[0] is the text // item[1] is the x coordinate to fill the text at // item[2] is the y coordinate to fill the text at ctx.fillText(item[0], item[1], item[2]); })

And we end up with wrapped text:

Now we can wrap text in canvas. The final code for the example above is shown below:

let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d'); canvas.width = 1200; canvas.height = 800; // @description: wrapText wraps HTML canvas text onto a canvas of fixed width // @param ctx - the context for the canvas we want to wrap text on // @param text - the text we want to wrap. // @param x - the X starting point of the text on the canvas. // @param y - the Y starting point of the text on the canvas. // @param maxWidth - the width at which we want line breaks to begin - i.e. the maximum width of the canvas. // @param lineHeight - the height of each line, so we can space them below each other. // @returns an array of [ lineText, x, y ] for all lines const wrapText = function(ctx, text, x, y, maxWidth, lineHeight) { // First, start by splitting all of our text into words, but splitting it into an array split by spaces let words = text.split(' '); let line = ''; // This will store the text of the current line let testLine = ''; // This will store the text when we add a word, to test if it's too long let lineArray = []; // This is an array of lines, which the function will return // Lets iterate over each word for(var n = 0; n < words.length; n++) { // Create a test line, and measure it.. testLine += `${words[n]} `; let metrics = ctx.measureText(testLine); let testWidth = metrics.width; // If the width of this test line is more than the max width if (testWidth > maxWidth && n > 0) { // Then the line is finished, push the current line into "lineArray" lineArray.push([line, x, y]); // Increase the line height, so a new line is started y += lineHeight; // Update line and test line to use this word as the first word on the next line line = `${words[n]} `; testLine = `${words[n]} `; } else { // If the test line is still less than the max width, then add the word to the current line line += `${words[n]} `; } // If we never reach the full max width, then there is only one line.. so push it into the lineArray so we return something if(n === words.length - 1) { lineArray.push([line, x, y]); } } // Return the line array return lineArray; } // Add gradient let grd = ctx.createLinearGradient(0, 1200, 800, 0); grd.addColorStop(0, '#00a0ff'); grd.addColorStop(1, '#12cba6'); ctx.fillStyle = grd; ctx.fillRect(0, 0, 1200, 800); // More text ctx.font = '700 95px Helvetica'; ctx.fillStyle = 'white'; let wrappedText = wrapText(ctx, "This line is way too long. It's going to overflow - but it should line break.", 85, 200, 1050, 140); wrappedText.forEach(function(item) { ctx.fillText(item[0], item[1], item[2]); })

Conclusion

Although we have to write a custom function to wrap text in HTML canvas, it's not too hard when you understand how it works. I hope you've enjoyed this guide on how to wrap text with HTML canvas. For more on HTML canvas, check out my full length guide here.

Last Updated Wednesday, 23 March 2022
Part of Series: HTML Canvas
Johnny Simpson
Johnny Simpson

More Tips and Tricks HTML

Subscribe for Weekly Dev Tips

Subscribe to our weekly newsletter, to stay up to date with our latest web development and software engineering posts via email. You can opt out at any time.

Not a valid email