文本换行在<canvas>元素</canvas>中

时间:2010-05-29 18:16:52

标签: javascript html5 canvas text

我正在尝试使用<canvas>元素在图像上添加文字。首先绘制图像,然后在图像上绘制文本。到目前为止一切都很好。

但是我遇到的问题是,如果文本太长,它会在画布的开头和结尾处被切断。我不打算调整画布大小,但我想知道如何将长文本包装成多行,以便显示所有内容。有人能指出我正确的方向吗?

10 个答案:

答案 0 :(得分:27)

@ 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()函数一个单词(没有任何空格),它将返回一个空数组,而不是一行数组。

在我们调查的时候,我们发现了另一个(更微妙的)错误,其中的行可能会比它们应该稍长一些,因为原始代码在测量行的长度时没有考虑空格。 / p>

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

答案 1 :(得分:23)

一种可能的方法(未完全测试,但现在它完美地工作)

    /**
     * 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;
}

答案 2 :(得分:7)

这是我对它的反应......我读了@ mizar的答案,并对它进行了一些改动......在一点点帮助下我能够得到它。

  

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

以下是示例用法。 http://jsfiddle.net/9PvMU/1/ - 这个脚本也可以看到here并最终成为我最终使用的...这个函数假设父范围内有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;
}

答案 3 :(得分:6)

context.measureText(text).width正是您要找的......

答案 4 :(得分:1)

查看https://developer.mozilla.org/en/Drawing_text_using_a_canvas#measureText%28%29

如果您可以看到所选文本,并且看到它比画布宽,则可以删除文字,直到文字足够短。使用删除的单词,您可以从第二行开始并执行相同的操作。

当然,这不会非常有效,所以你可以通过不删除一个单词来改进它,但如果你看到文本比画布宽度宽得多,你就可以改进它。

我没有研究,但也许他们甚至是为你做这件事的javascript库

答案 5 :(得分:1)

从这里的脚本:http://www.html5canvastutorials.com/tutorials/html5-canvas-wrap-text-tutorial/

我已经扩展到包括段落支持。使用\ n表示新行。

function wrapText(context, text, x, y, line_width, line_height)
{
    var line = '';
    var paragraphs = text.split('\n');
    for (var i = 0; i < paragraphs.length; i++)
    {
        var words = paragraphs[i].split(' ');
        for (var n = 0; n < words.length; n++)
        {
            var testLine = line + words[n] + ' ';
            var metrics = context.measureText(testLine);
            var testWidth = metrics.width;
            if (testWidth > line_width && n > 0)
            {
                context.fillText(line, x, y);
                line = words[n] + ' ';
                y += line_height;
            }
            else
            {
                line = testLine;
            }
        }
        context.fillText(line, x, y);
        y += line_height;
        line = '';
    }
}

文本可以这样格式化:

var text = 
[
    "Paragraph 1.",
    "\n\n",
    "Paragraph 2."
].join("");

使用:

wrapText(context, text, x, y, line_width, line_height);

取代

context.fillText(text, x, y);

答案 6 :(得分:1)

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

 <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

答案 7 :(得分:0)

答案 8 :(得分:0)

这应该从文本框中正确显示行: -

ng new some_project_name --style=scss

然后在画布上绘制获取的线条: -

 function fragmentText(text, maxWidth) {
    var lines = text.split("\n");
    var fittingLines = [];
    for (var i = 0; i < lines.length; i++) {
        if (canvasContext.measureText(lines[i]).width <= maxWidth) {
            fittingLines.push(lines[i]);
        }
        else {
            var tmp = lines[i];
            while (canvasContext.measureText(tmp).width > maxWidth) {
                tmp = tmp.slice(0, tmp.length - 1);
            }
            if (tmp.length >= 1) {
                var regex = new RegExp(".{1," + tmp.length + "}", "g");
                var thisLineSplitted = lines[i].match(regex);
                for (var j = 0; j < thisLineSplitted.length; j++) {
                    fittingLines.push(thisLineSplitted[j]);
                }
            }
        }
    }
    return fittingLines;

答案 9 :(得分:0)

我使用here发布自己的版本,因为这里的答案对我来说不够。在我的案例中需要衡量第一个词,以便能够拒绝来自小帆布区域的太长的单词。我需要支持“休息+空间”,“空间+休息”等。或双击/段落组合。

wrapLines: function(ctx, text, maxWidth) {
    var lines = [],
        words = text.replace(/\n\n/g,' ` ').replace(/(\n\s|\s\n)/g,'\r')
        .replace(/\s\s/g,' ').replace('`',' ').replace(/(\r|\n)/g,' '+' ').split(' '),
        space = ctx.measureText(' ').width,
        width = 0,
        line = '',
        word = '',
        len = words.length,
        w = 0,
        i;
    for (i = 0; i < len; i++) {
        word = words[i];
        w = word ? ctx.measureText(word).width : 0;
        if (w) {
            width = width + space + w;
        }
        if (w > maxWidth) {
            return [];
        } else if (w && width < maxWidth) {
            line += (i ? ' ' : '') + word;
        } else {
            !i || lines.push(line !== '' ? line.trim() : '');
            line = word;
            width = w;
        }
    }
    if (len !== i || line !== '') {
        lines.push(line);
    }
    return lines;
}

它支持换行符或段落符号的任何变体,删除双重空格,以及前导或尾随段落符号。如果文本不合适,它将返回一个空数组。或者准备好绘制一系列线条。