如何在HTML5画布中居中(等宽字体)文本

30

我希望能够在可以计算的矩形区域内居中单行文本。在2D画布上,唯一需要做的事情就是居中一个宽度未知的东西。

我听说可以通过在HTML容器中创建文本,然后调用jQuery的width()函数来解决这个问题,但我没有正确处理文档主体的瞬时添加,得到了0的宽度。

如果我有一行单独的文本,远远比屏幕上填充大部分宽度的文本要短,那么我如何知道在我所知道的字体大小下它的宽度?


在读取元素的宽度之前,不要将"display: none"设置为该元素。 - nrodic
7个回答

60
你可以通过使用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);

实时演示

更详细的演示


@ChristosHayward ...除了它不再被接受。 - DavidsKanal
@DavidsKanal 嗯...他的评论是在2012年的;) - Loktar
2
@Loktar 这感觉就像是一台时光机 :D - DavidsKanal
你是如何将100作为文本的中心的? - B''H Bi'ezras -- Boruch Hashem
100 是高度中心,这里将 canvas.height 设置为 200,如果我把它做成ctx.fillText(textString , (canvas.width/2) - (textWidth / 2), canvas.height/2);可能会看起来更好。 - Loktar

45
如果您并不一定需要文本的宽度,只是想要将文本居中,您可以这样做。
  canvas_context.textBaseline = 'middle';
  canvas_context.textAlign = "center";

应该将文本垂直和水平居中显示。

24

developer.mozilla.orgtextAlign的描述中指出,文本的对齐是基于上下文的fillText()方法的x值。也就是说,该属性不会在画布中水平居中文本,而是将文本围绕给定的x坐标居中。类似地,textBaseLine也是如此。

因此,为了使文本在两个方向上居中,我们需要设置这两个属性并将文本定位于画布的中心。

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

ctx.fillText('Game over', canvas_width/2, canvas_height/2);

1
好评论!API没有说ctx.textAlign = 'center'是从ctx.fillText('text', x, y)的x值居中的...研究了好几个小时... - SNS - Web et Informatique

3

要水平居中,我们使用 ctx.textBaseline = 'middle'; ctx.textAlign = 'center';。但是要垂直居中,我们需要使用 ctx.measureText

(actualBoundingBoxAscent - actualBoundingBoxDescent) / 2 很重要,因为对于不同字体的文本,其垂直对齐方式可能会有所不同。

// https://dev59.com/Zmoy5IYBdhLWcg3wN7e8#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>


1
您可以使用 ctx.textAlign = "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);

你是如何确定 "80" 是画布中心的? - B''H Bi'ezras -- Boruch Hashem

0

对于那些想知道如何正确对齐高度的人,因为measureText()不返回高度!

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>


你是如何确定“size”大于4的呢? - B''H Bi'ezras -- Boruch Hashem
对于如何做到这一点,可能需要尝试和错误,至于为什么,我无法精确地告诉你。不过这可能是一个很好的单独问题。 - NVRM

-6

我不明白这与所提出的问题有什么关系。他想要获取大小,以便将它们居中在画布中。 - Loktar
1
我认为他的答案与JonathanHayward提到的“变通方法”有关,但在这种情况下直接向画布询问文本的宽度(就像你的答案中所述)更好(也更可靠)。 - Roimer
Loktar,阅读问题:“我没有正确处理文档主体的瞬时添加,导致宽度为0。”--那是因为你无法测量隐藏的内容。另一种选择是使其可见但在屏幕外,这样用户就看不到它,然后再进行测量。 - Diodeus - James MacFarlane

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接