HTML5 canvas ctx.fillText 不会换行吗?

IT技术 javascript html canvas line break
2021-01-15 20:49:30

如果文本包含“\n”,我似乎无法向画布添加文本。我的意思是,换行符不显示/工作。

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

上面的代码将"s ome \n <br/> thing"在一行上绘制, 。

这是fillText的限制还是我做错了?"\n" 就在那里,没有打印出来,但它们也不起作用。

6个回答

如果您只想处理文本中的换行符,您可以通过在换行符处拆分文本并多次调用来模拟它 fillText()

类似于http://jsfiddle.net/BaG4J/1/

var c = document.getElementById('c').getContext('2d');
c.font = '11px Courier';
    console.log(c);
var txt = 'line 1\nline 2\nthird line..';
var x = 30;
var y = 30;
var lineheight = 15;
var lines = txt.split('\n');

for (var i = 0; i<lines.length; i++)
    c.fillText(lines[i], x, y + (i*lineheight) );
canvas{background-color:#ccc;}
<canvas id="c" width="150" height="150"></canvas>


我刚刚http://jsfiddle.net/BaG4J/2/ 上做了一个包装概念证明(在指定宽度的绝对包装。没有处理单词破坏,但
示例

var c = document.getElementById('c').getContext('2d');
c.font = '11px Courier';

var txt = 'this is a very long text to print';

printAt(c, txt, 10, 20, 15, 90 );


function printAt( context , text, x, y, lineHeight, fitWidth)
{
    fitWidth = fitWidth || 0;
    
    if (fitWidth <= 0)
    {
         context.fillText( text, x, y );
        return;
    }
    
    for (var idx = 1; idx <= text.length; idx++)
    {
        var str = text.substr(0, idx);
        console.log(str, context.measureText(str).width, fitWidth);
        if (context.measureText(str).width > fitWidth)
        {
            context.fillText( text.substr(0, idx-1), x, y );
            printAt(context, text.substr(idx-1), x, y + lineHeight, lineHeight,  fitWidth);
            return;
        }
    }
    context.fillText( text, x, y );
}
canvas{background-color:#ccc;}
<canvas id="c" width="150" height="150"></canvas>


和一个自动换行(在空格处打断)的概念证明。
示例在http://jsfiddle.net/BaG4J/5/

var c = document.getElementById('c').getContext('2d');
c.font = '11px Courier';

var txt = 'this is a very long text. Some more to print!';

printAtWordWrap(c, txt, 10, 20, 15, 90 );


function printAtWordWrap( context , text, x, y, lineHeight, fitWidth)
{
    fitWidth = fitWidth || 0;
    
    if (fitWidth <= 0)
    {
        context.fillText( text, x, y );
        return;
    }
    var words = text.split(' ');
    var currentLine = 0;
    var idx = 1;
    while (words.length > 0 && idx <= words.length)
    {
        var str = words.slice(0,idx).join(' ');
        var w = context.measureText(str).width;
        if ( w > fitWidth )
        {
            if (idx==1)
            {
                idx=2;
            }
            context.fillText( words.slice(0,idx-1).join(' '), x, y + (lineHeight*currentLine) );
            currentLine++;
            words = words.splice(idx-1);
            idx = 1;
        }
        else
        {idx++;}
    }
    if  (idx > 0)
        context.fillText( words.join(' '), x, y + (lineHeight*currentLine) );
}
canvas{background-color:#ccc;}
<canvas id="c" width="150" height="150"></canvas>


在第二个和第三个示例中,我使用的measureText()方法显示打印时字符串的长度(以像素为单位)。

如果你需要一个长的、对齐的文本,你为什么要使用画布?
2021-03-23 20:49:30
如何证明整个长文本?
2021-04-10 20:49:30

恐怕这是 Canvas' 的限制fillText没有多线支持。更糟糕的是,没有内置的方法来测量线高,只有宽度,让自己更难!

很多人都编写了自己的多线支持,其中最著名的项目可能是Mozilla Skywriter

您需要做的要点是多次fillText调用,同时每次将文本的高度添加到 y 值。(我相信,测量​​ M 的宽度是 Skywriter 人们用来近似文本的方法。)

也许actualBoundingBoxAscent可以用来测量行高。
2021-03-29 20:49:30
老实说,我不会对 fillText() 被“改进”以支持这一点而屏住呼吸,因为我觉得这就是它的用途(多次调用并自己计算 yOffset)。我认为画布 API 的强大之处在于它将较低级别的绘图功能与您已经可以做的事情(执行必要的测量)分开。此外,您只需提供以像素为单位的文本大小即可知道文本高度;换句话说:context.font = "16px Arial"; - 你有那里的高度;宽度是唯一一个动态的。
2021-04-03 20:49:30
一些额外的属性measureText()已加入我认为可以解决这个问题。Chrome 有一个标志来启用它们,但其他浏览器还没有……还没有!
2021-04-03 20:49:30
谢谢!我觉得这会很麻烦……很高兴知道 SKYWRITER,但我只会“等待”直到 fillText() 得到改进。就我而言,这不是一个非常重要的交易。哈,没有行高,好像有人故意那样做的。:D
2021-04-06 20:49:30
@SWdV 只是要清楚,这些已经在规范中多年了,直到我们有足够广泛的采用才能使用:(
2021-04-06 20:49:30

也许来这个派对有点晚,但我发现以下教程非常适合在画布上包装文本。

http://www.html5canvastutorials.com/tutorials/html5-canvas-wrap-text-tutorial/

从那以后,我能够想到让多行工作(对不起,拉米雷斯,你的不适合我!)。我在画布中包装文本的完整代码如下:

<script type="text/javascript">

     // http: //www.html5canvastutorials.com/tutorials/html5-canvas-wrap-text-tutorial/
     function wrapText(context, text, x, y, maxWidth, lineHeight) {
        var cars = text.split("\n");

        for (var ii = 0; ii < cars.length; ii++) {

            var line = "";
            var words = cars[ii].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 > maxWidth) {
                    context.fillText(line, x, y);
                    line = words[n] + " ";
                    y += lineHeight;
                }
                else {
                    line = testLine;
                }
            }

            context.fillText(line, x, y);
            y += lineHeight;
        }
     }

     function DrawText() {

         var canvas = document.getElementById("c");
         var context = canvas.getContext("2d");

         context.clearRect(0, 0, 500, 600);

         var maxWidth = 400;
         var lineHeight = 60;
         var x = 20; // (canvas.width - maxWidth) / 2;
         var y = 58;


         var text = document.getElementById("text").value.toUpperCase();                

         context.fillStyle = "rgba(255, 0, 0, 1)";
         context.fillRect(0, 0, 600, 500);

         context.font = "51px 'LeagueGothicRegular'";
         context.fillStyle = "#333";

         wrapText(context, text, x, y, maxWidth, lineHeight);
     }

     $(document).ready(function () {

         $("#text").keyup(function () {
             DrawText();
         });

     });

    </script>

c我的画布的 ID在哪里text,我的文本框的 ID 在哪里。

正如您可能看到的那样,我使用的是非标准字体。只要您在操作画布之前在某些文本上使用了字体,您就可以使用 @font-face - 否则画布不会选择字体。

希望这可以帮助某人。

将文本分成几行,并分别绘制:

function fillTextMultiLine(ctx, text, x, y) {
  var lineHeight = ctx.measureText("M").width * 1.2;
  var lines = text.split("\n");
  for (var i = 0; i < lines.length; ++i) {
    ctx.fillText(lines[i], x, y);
    y += lineHeight;
  }
}

这是我的解决方案,修改此处已提供的流行 wrapText() 函数。我正在使用 JavaScript 的原型设计功能,以便您可以从画布上下文中调用该函数。

CanvasRenderingContext2D.prototype.wrapText = function (text, x, y, maxWidth, lineHeight) {

    var lines = text.split("\n");

    for (var i = 0; i < lines.length; i++) {

        var words = lines[i].split(' ');
        var line = '';

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

        this.fillText(line, x, y);
        y += lineHeight;
    }
}

基本用法:

var myCanvas = document.getElementById("myCanvas");
var ctx = myCanvas.getContext("2d");
ctx.fillStyle = "black";
ctx.font = "12px sans-serif";
ctx.textBaseline = "top";
ctx.wrapText("Hello\nWorld!",20,20,160,16);

这是我放在一起的演示:http : //jsfiddle.net/7RdbL/

像魅力一样工作。谢谢你。
2021-03-18 20:49:30