I'm drawing text in an HTML5 canvas on top of an image (a meme generator). I want to calculate a font size so that the string will span the entire width of the canvas.
So basically, is there a way in JavaScript to determine what font size I should use for some string of a certain font for a given width?
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...
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 iteration through the array elements will be O(n).
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.
I have the next code and it works perfectly for me. Maybe there is a little error but I don't need to have a list of font sizes. I just get the width with a font size and if it is too big I calculate a proportional size depending of the canvas width and padding (50)
ctx.font = FONT_SIZE+"pt Verdana"; var textWidth = ctx.measureText(markText).width; if(textWidth > canvas.width - 50) { ctx.font = parseInt(FONT_SIZE*(canvas.width-50)/textWidth)+"pt Verdana"; textWidth = ctx.measureText(markText).width; } ctx.textBaseline = "middle"; ctx.fillStyle = "rgba(255,255,255,0.5)"; ctx.strokeStyle = "rgba(0,0,0,0.5)"; var xT = image.width / 2 - textWidth / 2; var yT = image.height / 2; ctx.fillText(markText, xT, yT); ctx.strokeText(markText, xT, yT);