29

I would like to be able to center single lines of text within rectangular areas I can calculate. The one thing I have expected to do in 2D geometry on a canvas is to center something whose width is unknown to you.

I have heard as a workaround that you can create the text in an HTML container and then call jQuery's width() function, but I ?didn't correctly handle the momentary addition to the document's body? and got a width of 0.

If I have a single line of text, significantly shorter than would fill most of the width in a screen, how can I tell how wide it will be on a canvas at a font size I know?

ggorlen
  • 44,755
  • 7
  • 76
  • 106
Christos Hayward
  • 5,777
  • 17
  • 58
  • 113

7 Answers7

57

You can do this by using measureText

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

canvas.width = 400;
canvas.height = 200;

ctx.fillStyle = "#003300";
ctx.font = '20px sans-serif';

var textString = "Hello look at me!!!",
    textWidth = ctx.measureText(textString ).width;


ctx.fillText(textString , (canvas.width/2) - (textWidth / 2), 100);

Live Demo

More elaborate demo

robd
  • 9,646
  • 5
  • 40
  • 59
Loktar
  • 34,764
  • 7
  • 90
  • 104
43

If you don't necesserilly need a width of the text but just want to center text you can do

  canvas_context.textBaseline = 'middle';
  canvas_context.textAlign = "center";

Which should put a text centered both vertically and horizontally.

Marcin Raczkowski
  • 1,500
  • 1
  • 18
  • 26
24

developer.mozilla.org states in the textAlign description that the alignment is based on the x value of the context's fillText() method. That is, the property does not center the text in the canvas horizontally; it centers the text around the given x coordinate. Similar applies for the textBaseLine.

So in order to center the text in both directions, we need to set those two properties and position the text in the middle of the canvas.

ctx.textBaseline = 'middle'; 
ctx.textAlign = 'center'; 

ctx.fillText('Game over', canvas_width/2, canvas_height/2);
Jan Bodnar
  • 10,969
  • 6
  • 68
  • 77
3

For centering horizontally we use ctx.textBaseline = 'middle'; ctx.textAlign = 'center';. But to center vertically we need to use ctx.measureText.

(actualBoundingBoxAscent - actualBoundingBoxDescent) / 2 is important, because for different fonts text can be differently aligned vertically.

// https://stackoverflow.com/questions/8696631/canvas-drawings-like-lines-are-blurry/59143499#59143499
function setCanvas(canvas, w, h) {
    canvas.style.width = w + 'px';
    canvas.style.height = h + 'px';
    const scale = window.devicePixelRatio;
    canvas.width = w * scale;
    canvas.height = h * scale;
    const ctx = canvas.getContext('2d');
    ctx.scale(scale, scale);
}

const width = 160;
const height = 160;
const fontSize = 100;
const text = 'A';

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

setCanvas(canvas, width, height); 

ctx.fillStyle = 'red';
ctx.fillRect(0, 0, canvas.width, canvas.height);

ctx.fillStyle = 'white';
ctx.font = `${fontSize}px sans-serif`;
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
let { actualBoundingBoxAscent, actualBoundingBoxDescent } = ctx.measureText(text);
ctx.fillText(text, width / 2, height / 2 + (actualBoundingBoxAscent - actualBoundingBoxDescent) / 2);
canvas{
  outline: 1px black solid
 }
<canvas><canvas>
rofrol
  • 14,438
  • 7
  • 79
  • 77
1

You can use ctx.textAlign = "center"; to align item to center

var canva = document.getElementById("canvas");
ctx.textAlign = "center"; // To Align Center
ctx.font = "40px Arial"; // To change font size and type
ctx.fillStyle = '#313439'; // To give font 
ctx.fillText("Text Center", 150 ,80);
Hitesh Subnani
  • 589
  • 8
  • 11
0

For those wondering around how to properly align the height as well, since measureText() does not return the height (!).

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");

const centered = (text, size) => {
  ctx.font = size + 'px monospace';
  let s = ctx.measureText(text).width;
  ctx.fillText(text, canvas.width / 2 - s / 2, canvas.height / 2 + size / 4);
}  
    
/* Just for the demo, really! 
 * Please don't do this!
 * Use proper animation frames requests
 * and clear timeout(s)!!! */
[["is", 40], ["able",45], ["<-- to -->",50], ["FLEX", 55], [" SMOOTH!", 58], ["FLEX!!", 62], ["FLEX!!!", 63] ].forEach((e) => {
  setTimeout(() => {
    ctx.clearRect(0, 0, canvas.width, canvas.height)
    centered(e[0], e[1])
  }, e[1]*100)
})

// Intro test
centered("CANFLEX", 50)
canvas{
  outline: 1px black solid
 }
<canvas><canvas>
NVRM
  • 11,480
  • 1
  • 88
  • 87
-6

What you do is render your text off-screen using a negative text-indent, then grab the size.

text-indent: -9999px

See: hiding text using "text-indent"

Community
  • 1
  • 1
Diodeus - James MacFarlane
  • 112,730
  • 33
  • 157
  • 176
  • I don't understand how this has anything to do with the question being asked. He's looking to get the size so he can center them within canvas. – Loktar Dec 08 '12 at 01:17
  • 1
    In think his answer is related on the _workaround_ JonathanHayward mentioned, but asking the text's width directly to the canvas (as in your answer) is better (and more reliable) in this case. – Roimer Dec 08 '12 at 03:04
  • Loktar, read the question "I didn't correctly handle the momentary addition to the document's body and got a width of 0." -- That's because you can't measure hidden content. The alternative is to make it visible but OFF SCREEN so the user doesn't see it, then measure it. – Diodeus - James MacFarlane Dec 11 '12 at 17:13