简体   繁体   中英

Center (proportional font) text in an HTML5 canvas

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?

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

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.

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);

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);

For centering horizontally we use ctx.textBaseline = 'middle'; ctx.textAlign = 'center'; 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>

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>

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"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM