Determine width of string in HTML5 canvas

The fillText() method has an optional fourth argument, which is the max width to render the string.

MDN's documentation says...

maxWidth

Optional; the maximum width to draw. If specified, and the string is computed to be wider than this width, the font is adjusted to use a more horizontally condensed font (if one is available or if a reasonably readable one can be synthesized by scaling the current font horizontally) or a smaller font.

However, at the time of writing, this argument isn't supported well cross browser, which leads to the second solution using measureText() to determine the dimensions of a string without rendering it.

var width = ctx.measureText(text).width;

Here is how I may do it...

var canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d');

// Font sizes must be in descending order. You may need to use `sort()`
// if you can't guarantee this, e.g. user input.
var fontSizes = [72, 36, 28, 14, 12, 10, 5, 2],
    text = 'Measure me!';

// Default styles.
ctx.textBaseline = 'top';
ctx.fillStyle = 'blue';

var textDimensions,
    i = 0;

do {
   ctx.font = fontSizes[i++] + 'px Arial';
   textDimensions = ctx.measureText(text);        
} while (textDimensions.width >= canvas.width);

ctx.fillText(text, (canvas.width - textDimensions.width) / 2, 10);​

jsFiddle.

I have a list of font sizes in descending order and I iterate through the list, determining if the rendered text will fit within the canvas dimensions.

If it will, I render the text center aligned. If you must have padding on the left and right of the text (which will look nicer), add the padding value to the textDimensions.width when calculating if the text will fit.

If you have a long list of font sizes to try, you'd be better off using a binary search algorithm. This will increase the complexity of your code, however.

For example, if you have 200 font sizes, the linear O(n) iteration through the array elements could be quite slow.

The binary chop should be O(log n).

Here is the guts of the function.

var textWidth = (function me(fontSizes, min, max) {

    var index = Math.floor((min + max) / 2);

    ctx.font = fontSizes[index] + 'px Arial';

    var textWidth = ctx.measureText(text).width;

    if (min > max) {
        return textWidth;
    }

    if (textWidth > canvas.width) {
        return me(fontSizes, min, index - 1);
    } else {
        return me(fontSizes, index + 1, max);
    }

})(fontSizes, 0, fontSizes.length - 1);

jsFiddle.


  1. Write All Things You Want For Text. ( Ex. ctx.font )

  2. Before Writing Actual Text Use ctx.measureText("myText").width To get Width Of Text, Because We Have Called It After Making Changes On ctx, It Will Be Different Each Time When We Change ctx Properties (ex. font)

  3. Now We Will Scale It From Middle.

     ctx.translate(midPoint_X_Of_Text, midPoint_Y_Of_Text);
     ctx.scale(desiredWidth/measuredWidth, desiredWidth/measuredWidth);
     ctx.translate(-midPoint_X_Of_Text, -midPoint_Y_Of_Text);
    
  4. Write Text ctx.fillText() or ctx.strokeText()

  5. Reverse All Changes By ctx.save() and ctx.restore() Or Manually like setTransform()

var can = document.getElementById('myCan');
var ctx = can.getContext("2d");

var desiredWidth = can.width;
var myText = "Hello How Are You?"

function draw(){
  ctx.font = can.height/2 + "px verdana";
  ctx.fillStyle = "red";
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";
  let measuredWidth = ctx.measureText(myText).width;
  let ratio = desiredWidth/measuredWidth;
  ctx.translate(can.width/2,can.height/2);
  ctx.scale(ratio,ratio);
  ctx.translate(-can.width/2,-can.height/2);
  ctx.fillText(myText,can.width/2,can.height/2);
  ctx.setTransform();
}

draw();

let myInput = document.getElementById('myInput');
myInput.addEventListener('input',(e)=>{
    ctx.clearRect(0,0,can.width,can.height);
        myText = myInput.value;
        draw();
})
<html>
<body>
  <center>
    <input id="myInput" value="Hello How Are You?"/><br>
    <canvas id="myCan" height="300" width=300 style="border:black solid 2px;">
    </canvas>
  </center>
</body>
</html>