<canvas> 元素中的文本换行

IT技术 javascript html canvas text
2021-03-15 07:59:41

我正在尝试使用该<canvas>元素在图像上添加文本首先绘制图像,然后在图像上绘制文本。到现在为止还挺好。

但是我面临的一个问题是,如果文本太长,它会在开始和结束时被画布截断。我不打算调整画布的大小,但我想知道如何将长文本包装成多行,以便显示所有内容。任何人都可以指出我正确的方向吗?

6个回答

@mizar 答案的更新版本,修复了一个严重错误和一个小错误。

function getLines(ctx, text, maxWidth) {
    var words = text.split(" ");
    var lines = [];
    var currentLine = words[0];

    for (var i = 1; i < words.length; i++) {
        var word = words[i];
        var width = ctx.measureText(currentLine + " " + word).width;
        if (width < maxWidth) {
            currentLine += " " + word;
        } else {
            lines.push(currentLine);
            currentLine = word;
        }
    }
    lines.push(currentLine);
    return lines;
}

我们已经使用这段代码有一段时间了,但今天我们试图弄清楚为什么有些文本没有绘制,我们发现了一个错误!

事实证明,如果你给 getLines() 函数一个单词(没有任何空格),它将返回一个空数组,而不是一个单行数组。

在我们对此进行调查时,我们发现了另一个(更微妙的)错误,其中的行最终可能比应有的长度略长,因为原始代码在测量行的长度时没有考虑空格。

我们的更新版本适用于我们投入的所有内容,就在上面。如果您发现任何错误,请告诉我!

这不适用于带有块字符的语言,例如中文。
2021-04-25 07:59:41
@Jason:这很容易修复,只需将其包装在您自己的函数中,例如: function getLinesForParagraphs(ctx, text, maxWidth) { return text.split("\n").map(para => getLines(ctx, para, maxWidth)).reduce([], (a, b) => a.concat(b)) }
2021-04-30 07:59:41
我不知道这是否是您的意图,但它不处理换行符。
2021-05-05 07:59:41
@JunleLi:结果证明为所有语言正确换行很复杂[1],但是通过执行var words = text.split(""),而不是在空格上拆分,您可以更接近带有块字符的语言(请注意,这将在单词中间拆分拉丁字母语言)。更好的解决方案是解析单词并根据语言进行拆分(因此块字符和拉丁单词可以混合并正确分解)。真正正确的解决方案需要使用字典。[1] w3c.github.io/i18n-drafts/articles/typography/linebreak.en
2021-05-14 07:59:41

一种可能的方法(未完全测试,但就目前而言,它运行良好)

    /**
     * Divide an entire phrase in an array of phrases, all with the max pixel length given.
     * The words are initially separated by the space char.
     * @param phrase
     * @param length
     * @return
     */
function getLines(ctx,phrase,maxPxLength,textStyle) {
    var wa=phrase.split(" "),
        phraseArray=[],
        lastPhrase=wa[0],
        measure=0,
        splitChar=" ";
    if (wa.length <= 1) {
        return wa
    }
    ctx.font = textStyle;
    for (var i=1;i<wa.length;i++) {
        var w=wa[i];
        measure=ctx.measureText(lastPhrase+splitChar+w).width;
        if (measure<maxPxLength) {
            lastPhrase+=(splitChar+w);
        } else {
            phraseArray.push(lastPhrase);
            lastPhrase=w;
        }
        if (i===wa.length-1) {
            phraseArray.push(lastPhrase);
            break;
        }
    }
    return phraseArray;
}
如果短语仅包含一个单词,则返回一个空数组。
2021-04-22 07:59:41
作为一个建议 - 在做了一些 perfs 之后,我发现一次测量一个字符并缓存每个字符的结果,然后将单个字符的宽度加在一起,尽管它看起来很昂贵,但实际上比 canvas measureText 快
2021-05-08 07:59:41

这是我的看法……我阅读了@mizar 的回答并对其进行了一些修改……在一些帮助下,我得到了这个答案。

代码已删除,请参阅小提琴。

这是示例用法。http://jsfiddle.net/9PvMU/1/ - 这个脚本也可以在这里看到并最终成为我最后使用的......这个函数假设ctx在父作用域中可用......如果不是你总是可以传进去。


编辑

这篇文章很旧,有我仍在修补的功能版本。到目前为止,这个版本似乎已经满足了我的需求,我希望它可以帮助其他人。


编辑

我注意到这段代码中有一个小错误。我花了一些时间来解决它,但这里已更新。我自己测试了它,现在它似乎按预期工作。

function fragmentText(text, maxWidth) {
    var words = text.split(' '),
        lines = [],
        line = "";
    if (ctx.measureText(text).width < maxWidth) {
        return [text];
    }
    while (words.length > 0) {
        var split = false;
        while (ctx.measureText(words[0]).width >= maxWidth) {
            var tmp = words[0];
            words[0] = tmp.slice(0, -1);
            if (!split) {
                split = true;
                words.splice(1, 0, tmp.slice(-1));
            } else {
                words[1] = tmp.slice(-1) + words[1];
            }
        }
        if (ctx.measureText(line + words[0]).width < maxWidth) {
            line += words.shift() + " ";
        } else {
            lines.push(line);
            line = "";
        }
        if (words.length === 0) {
            lines.push(line);
        }
    }
    return lines;
}
它无法识别“ENTER”值。并且如果用户输入超出 maxWidth 允许的长单词/字符,则不会为下一个单词生成空间。有什么建议吗?
2021-04-20 07:59:41
@ivory-santos maxWidth 修整空间现已修复。
2021-04-25 07:59:41
它不应该识别输入值,它不是那样构建的。但是,如果你听了,我想你可以强制换一条线。你是对的,我以前从未注意到这个错误。我会研究它并更新我的答案。
2021-04-27 07:59:41

context.measureText(text).width 是你要找的...

尝试使用此脚本将文本包裹在画布上。

 <script>
  function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
    var words = text.split(' ');
    var line = '';

    for(var n = 0; n < words.length; n++) {
      var testLine = line + words[n] + ' ';
      var metrics = ctx.measureText(testLine);
      var testWidth = metrics.width;
      if (testWidth > maxWidth && n > 0) {
        ctx.fillText(line, x, y);
        line = words[n] + ' ';
        y += lineHeight;
      }
      else {
        line = testLine;
      }
    }
    ctx.fillText(line, x, y);
  }

  var canvas = document.getElementById('Canvas01');
  var ctx = canvas.getContext('2d');
  var maxWidth = 400;
  var lineHeight = 24;
  var x = (canvas.width - maxWidth) / 2;
  var y = 70;
  var text = 'HTML is the language for describing the structure of Web pages. HTML stands for HyperText Markup Language. Web pages consist of markup tags and plain text. HTML is written in the form of HTML elements consisting of tags enclosed in angle brackets (like <html>). HTML tags most commonly come in pairs like <h1> and </h1>, although some tags represent empty elements and so are unpaired, for example <img>..';

  ctx.font = '15pt Calibri';
  ctx.fillStyle = '#555555';

  wrapText(ctx, text, x, y, maxWidth, lineHeight);
  </script>
</body>

在这里查看演示 http://codetutorial.com/examples-canvas/canvas-examples-text-wrap