HTML5画布ctx.fillText为什么不换行?

139

如果文本中包含 "\n",我似乎无法将文本添加到画布上。我的意思是,换行符不会显示/起作用。

ctxPaint.fillText("s  ome \n \\n <br/> thing", x, y);

以上代码将在一行上绘制"s ome \n <br/> thing"

这是fillText的限制还是我的问题?"\n"存在但没有被打印出来,也不起作用。


1
你想在到达末尾时自动换行吗?还是只考虑文本中存在的换行符? - Gabriele Petrioli
将文本换行为多行。 - Tower
嗨,twodordan,这个限制在Chrome和Mozilla上都存在吗? 人们经常使用简单的HTML文本,并将其放置在画布上,例如position:absolute。 此外,您可以使用两个fillText并移动文本的Y原点以获取第二行。 - Tim
简而言之:要么多次调用 fillText() 并使用字体高度进行分隔,要么使用 https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/measureText - 或者使用下面非常复杂的“解决方案”之一,但不使用 TextMetrics... - Andrew
20个回答

6
@Gaby Petrioli 提供的代码可以自动换行(在空格处换行),非常有用。我对其进行了扩展,使其支持换行字符\n。另外,通常情况下,获取文本框的尺寸也是很有用的,因此multiMeasureText()返回宽度和高度两个参数。

你可以在这里查看代码:http://jsfiddle.net/jeffchan/WHgaY/76/


链接会过期,请将代码放在这个答案中,即使您有一个可用的链接。如果 jsfiddle 关闭,这个答案就会变得完全无用。 - Mike 'Pomax' Kamermans

4

有一个维护得很好的JS库叫做Canvas-Txt,它能够处理换行和文本包装。我发现它比这个线程中所有随机的代码片段都更实用。


4
这个问题并没有考虑canvas的工作原理。如果你想要一个换行,只需调整下一个ctx.fillText的坐标即可。
ctx.fillText("line1", w,x,y,z)
ctx.fillText("line2", w,x,y,z+20)

3
我创建了名为 wraptext 的库,帮助将文本包裹以适应视图的限制。
要包裹文本,我们需要实现两个逻辑:
- 行拟合:确保没有行超过视图宽度。可以使用 Canvas API 来测量文本宽度来完成此操作。 - 断行:确保在正确位置(换行机会)断开行。这个逻辑可能有不同的实现方式,但最好遵循这份 Unicode 文档,因为它支持所有语言。
虽然这个答案距离问题发布的时间有些久远,但我希望对那些遇到相同问题的人有所帮助。

3

我认为你仍然可以依靠CSS

ctx.measureText().height doesn’t exist.

幸运的是,通过CSS hack技巧(详见《排版度量》以了解使用CSS测量修复旧实现的更多方法),我们可以通过测量具有相同字体属性的带有offsetHeight属性的元素来找到文本的高度:
var d = document.createElement(”span”);
d.font = “20px arial”
d.textContent = “Hello world!”
var emHeight = d.offsetHeight;

from: http://www.html5rocks.com/en/tutorials/canvas/texteffects/


如果您有足够的内存来构建每次需要测量时都要使用的元素,那么这是一个不错的解决方案。您还可以使用 ctx.save(),然后 ctx.font = '12pt Arial',最后 parseInt(ctx.font, 10)。请注意,我在设置时使用了“pt”。它将转换为PX,并能够转化为字体高度的数字以供使用。 - Eric Hodonsky

2

我因为遇到同样的问题而发现了这个。我正在使用可变字体大小,所以这个方法考虑了这一点:

var texts=($(this).find('.noteContent').html()).split("<br>");
for (var k in texts) {
    ctx.fillText(texts[k], left, (top+((parseInt(ctx.font)+2)*k)));
}

.noteContent是用户编辑的可编辑div(嵌套在jQuery each函数中),ctx.font是"14px Arial"(请注意像素大小首先出现)


2
我认为这是不可能的,但是有一个解决办法是创建一个 <p> 元素,并使用Javascript将其定位。

是的,这正是我想要做的。只是使用 fillText()strokeText(),你可以做出超越 CSS 能力的事情。 - Tower
我还没有测试过这个,但我认为这可能是一个更好的解决方案——使用fillText()的其他解决方案会使文本无法被选择(或者可能无法粘贴)。 - Jerry Asher

1
这是我的函数,用于在画布上居中绘制多行文本(只换行,不断字)

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

let text = "Hello World \n Hello World 2222 \n AAAAA \n thisisaveryveryveryveryveryverylongword. "
ctx.font = "20px Arial";

fillTextCenter(ctx, text, 0, 0, c.width, c.height)

function fillTextCenter(ctx, text, x, y, width, height) {
    ctx.textBaseline = 'middle';
    ctx.textAlign = "center";

    const lines = text.match(/[^\r\n]+/g);
    for(let i = 0; i < lines.length; i++) {
        let xL = (width - x) / 2
        let yL =  y + (height / (lines.length + 1)) * (i+1)

        ctx.fillText(lines[i], xL, yL)
    }
}
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #000;"></canvas>

如果您想将文本大小适应画布,您还可以在这里进行检查。


0

Canvas元素不支持换行符'\n',制表符'\t'或<br/>标记。

试一试:

var newrow = mheight + 30;
ctx.fillStyle = "rgb(0, 0, 0)";
ctx.font = "bold 24px 'Verdana'";
ctx.textAlign = "center";
ctx.fillText("Game Over", mwidth, mheight); //first line
ctx.fillText("play again", mwidth, newrow); //second line 

或者可能是多行:

var textArray = new Array('line2', 'line3', 'line4', 'line5');
var rows = 98;
ctx.fillStyle = "rgb(0, 0, 0)";
ctx.font = "bold 24px 'Verdana'";
ctx.textAlign = "center";
ctx.fillText("Game Over", mwidth, mheight); //first line

for(var i=0; i < textArray.length; ++i) {
rows += 30;
ctx.fillText(textArray[i], mwidth, rows); 
}  

0

我针对这个问题的 ES5 解决方案:

var wrap_text = (ctx, text, x, y, lineHeight, maxWidth, textAlign) => {
  if(!textAlign) textAlign = 'center'
  ctx.textAlign = textAlign
  var words = text.split(' ')
  var lines = []
  var sliceFrom = 0
  for(var i = 0; i < words.length; i++) {
    var chunk = words.slice(sliceFrom, i).join(' ')
    var last = i === words.length - 1
    var bigger = ctx.measureText(chunk).width > maxWidth
    if(bigger) {
      lines.push(words.slice(sliceFrom, i).join(' '))
      sliceFrom = i
    }
    if(last) {
      lines.push(words.slice(sliceFrom, words.length).join(' '))
      sliceFrom = i
    }
  }
  var offsetY = 0
  var offsetX = 0
  if(textAlign === 'center') offsetX = maxWidth / 2
  for(var i = 0; i < lines.length; i++) {
    ctx.fillText(lines[i], x + offsetX, y + offsetY)
    offsetY = offsetY + lineHeight
  }
}

关于此问题的更多信息,请参见我的博客


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