DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Trending

  • Infrastructure as Code (IaC) Beyond the Basics
  • The Future of Java and AI: Coding in 2025
  • Integrating Model Context Protocol (MCP) With Microsoft Copilot Studio AI Agents
  • The Full-Stack Developer's Blind Spot: Why Data Cleansing Shouldn't Be an Afterthought

How to Wrap Text in HTML Canvas

Let's look at how to wrap text with HTML Canvas

By 
Johnny Simpson user avatar
Johnny Simpson
DZone Core CORE ·
Mar. 23, 22 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
2.4K Views

Join the DZone community and get the full member experience.

Join For Free

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:

JavaScript
 
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 that 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:
JavaScript
 
// @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:
JavaScript
 
// 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:

JavaScript
 
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.

Published at DZone with permission of Johnny Simpson, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!