【问题标题】:HTML5 canvas ctx.fillText won't do line breaks?HTML5 canvas ctx.fillText 不会换行?
【发布时间】:2011-05-27 14:19:52
【问题描述】:

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

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

上面的代码将在一行上绘制"s ome \n &lt;br/&gt; thing"

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

【问题讨论】:

标签: javascript html canvas line break


【解决方案1】:

如果您只想处理文本中的换行符,您可以通过在换行符处拆分文本并多次调用 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;}
&lt;canvas id="c" width="150" height="150"&gt;&lt;/canvas&gt;

我刚刚做了一个包装概念证明(在指定宽度处绝对包装。还没有处理破字
例如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;}
&lt;canvas id="c" width="150" height="150"&gt;&lt;/canvas&gt;

还有一个自动换行(在空格处打断)概念证明。
例如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;}
&lt;canvas id="c" width="150" height="150"&gt;&lt;/canvas&gt;

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

【讨论】:

  • 如何证明整个长文本的合理性?
  • 如果您需要一个长且两端对齐的文本,为什么要使用画布?
【解决方案2】:

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

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

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

【讨论】:

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

也许参加这个聚会有点晚了,但我发现下面的在画布上环绕文本的教程非常完美。

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 - 否则画布将不会拾取字体。

希望这对某人有所帮助。

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

      这是我的解决方案,修改此处已经介绍的流行 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/

      【讨论】:

      • 工作就像一个魅力。谢谢。
      【解决方案6】:

      我只是扩展了CanvasRenderingContext2D 添加了两个函数:mlFillText 和 mlStrokeText。

      你可以在GitHub找到最新版本:

      使用此功能,您可以在框中填充/描边 miltiline 文本。您可以垂直和水平对齐文本。 (它考虑了\n's 并且还可以证明文本的合理性)。

      原型是:

      function mlFillText(text,x,y,w,h,vAlign,hAlign,lineheight);
      function mlStrokeText(text,x,y,w,h,vAlign,hAlign,lineheight);
      

      vAlign 可以是:topcenterbutton

      hAlign 可以是:leftcenterrightjustify

      你可以在这里测试这个库:http://jsfiddle.net/4WRZj/1/

      这是库的代码:

      // Library: mltext.js
      // Desciption: Extends the CanvasRenderingContext2D that adds two functions: mlFillText and mlStrokeText.
      //
      // The prototypes are: 
      //
      // function mlFillText(text,x,y,w,h,vAlign,hAlign,lineheight);
      // function mlStrokeText(text,x,y,w,h,vAlign,hAlign,lineheight);
      // 
      // Where vAlign can be: "top", "center" or "button"
      // And hAlign can be: "left", "center", "right" or "justify"
      // Author: Jordi Baylina. (baylina at uniclau.com)
      // License: GPL
      // Date: 2013-02-21
      
      function mlFunction(text, x, y, w, h, hAlign, vAlign, lineheight, fn) {
          text = text.replace(/[\n]/g, " \n ");
          text = text.replace(/\r/g, "");
          var words = text.split(/[ ]+/);
          var sp = this.measureText(' ').width;
          var lines = [];
          var actualline = 0;
          var actualsize = 0;
          var wo;
          lines[actualline] = {};
          lines[actualline].Words = [];
          i = 0;
          while (i < words.length) {
              var word = words[i];
              if (word == "\n") {
                  lines[actualline].EndParagraph = true;
                  actualline++;
                  actualsize = 0;
                  lines[actualline] = {};
                  lines[actualline].Words = [];
                  i++;
              } else {
                  wo = {};
                  wo.l = this.measureText(word).width;
                  if (actualsize === 0) {
                      while (wo.l > w) {
                          word = word.slice(0, word.length - 1);
                          wo.l = this.measureText(word).width;
                      }
                      if (word === "") return; // I can't fill a single character
                      wo.word = word;
                      lines[actualline].Words.push(wo);
                      actualsize = wo.l;
                      if (word != words[i]) {
                          words[i] = words[i].slice(word.length, words[i].length);
                      } else {
                          i++;
                      }
                  } else {
                      if (actualsize + sp + wo.l > w) {
                          lines[actualline].EndParagraph = false;
                          actualline++;
                          actualsize = 0;
                          lines[actualline] = {};
                          lines[actualline].Words = [];
                      } else {
                          wo.word = word;
                          lines[actualline].Words.push(wo);
                          actualsize += sp + wo.l;
                          i++;
                      }
                  }
              }
          }
          if (actualsize === 0) lines[actualline].pop();
          lines[actualline].EndParagraph = true;
      
          var totalH = lineheight * lines.length;
          while (totalH > h) {
              lines.pop();
              totalH = lineheight * lines.length;
          }
      
          var yy;
          if (vAlign == "bottom") {
              yy = y + h - totalH + lineheight;
          } else if (vAlign == "center") {
              yy = y + h / 2 - totalH / 2 + lineheight;
          } else {
              yy = y + lineheight;
          }
      
          var oldTextAlign = this.textAlign;
          this.textAlign = "left";
      
          for (var li in lines) {
              var totallen = 0;
              var xx, usp;
              for (wo in lines[li].Words) totallen += lines[li].Words[wo].l;
              if (hAlign == "center") {
                  usp = sp;
                  xx = x + w / 2 - (totallen + sp * (lines[li].Words.length - 1)) / 2;
              } else if ((hAlign == "justify") && (!lines[li].EndParagraph)) {
                  xx = x;
                  usp = (w - totallen) / (lines[li].Words.length - 1);
              } else if (hAlign == "right") {
                  xx = x + w - (totallen + sp * (lines[li].Words.length - 1));
                  usp = sp;
              } else { // left
                  xx = x;
                  usp = sp;
              }
              for (wo in lines[li].Words) {
                  if (fn == "fillText") {
                      this.fillText(lines[li].Words[wo].word, xx, yy);
                  } else if (fn == "strokeText") {
                      this.strokeText(lines[li].Words[wo].word, xx, yy);
                  }
                  xx += lines[li].Words[wo].l + usp;
              }
              yy += lineheight;
          }
          this.textAlign = oldTextAlign;
      }
      
      (function mlInit() {
          CanvasRenderingContext2D.prototype.mlFunction = mlFunction;
      
          CanvasRenderingContext2D.prototype.mlFillText = function (text, x, y, w, h, vAlign, hAlign, lineheight) {
              this.mlFunction(text, x, y, w, h, hAlign, vAlign, lineheight, "fillText");
          };
      
          CanvasRenderingContext2D.prototype.mlStrokeText = function (text, x, y, w, h, vAlign, hAlign, lineheight) {
              this.mlFunction(text, x, y, w, h, hAlign, vAlign, lineheight, "strokeText");
          };
      })();
      

      这是使用示例:

      var c = document.getElementById("myCanvas");
      var ctx = c.getContext("2d");
      
      var T = "This is a very long line line with a CR at the end.\n This is the second line.\nAnd this is the last line.";
      var lh = 12;
      
      ctx.lineWidth = 1;
      
      ctx.mlFillText(T, 10, 10, 100, 100, 'top', 'left', lh);
      ctx.strokeRect(10, 10, 100, 100);
      
      ctx.mlFillText(T, 110, 10, 100, 100, 'top', 'center', lh);
      ctx.strokeRect(110, 10, 100, 100);
      
      ctx.mlFillText(T, 210, 10, 100, 100, 'top', 'right', lh);
      ctx.strokeRect(210, 10, 100, 100);
      
      ctx.mlFillText(T, 310, 10, 100, 100, 'top', 'justify', lh);
      ctx.strokeRect(310, 10, 100, 100);
      
      ctx.mlFillText(T, 10, 110, 100, 100, 'center', 'left', lh);
      ctx.strokeRect(10, 110, 100, 100);
      
      ctx.mlFillText(T, 110, 110, 100, 100, 'center', 'center', lh);
      ctx.strokeRect(110, 110, 100, 100);
      
      ctx.mlFillText(T, 210, 110, 100, 100, 'center', 'right', lh);
      ctx.strokeRect(210, 110, 100, 100);
      
      ctx.mlFillText(T, 310, 110, 100, 100, 'center', 'justify', lh);
      ctx.strokeRect(310, 110, 100, 100);
      
      ctx.mlFillText(T, 10, 210, 100, 100, 'bottom', 'left', lh);
      ctx.strokeRect(10, 210, 100, 100);
      
      ctx.mlFillText(T, 110, 210, 100, 100, 'bottom', 'center', lh);
      ctx.strokeRect(110, 210, 100, 100);
      
      ctx.mlFillText(T, 210, 210, 100, 100, 'bottom', 'right', lh);
      ctx.strokeRect(210, 210, 100, 100);
      
      ctx.mlFillText(T, 310, 210, 100, 100, 'bottom', 'justify', lh);
      ctx.strokeRect(310, 210, 100, 100);
      
      ctx.mlStrokeText("Yo can also use mlStrokeText!", 0 , 310 , 420, 30, 'center', 'center', lh);
      

      【讨论】:

      • Uncaught ReferenceError: Words is not defined 如果我尝试更改字体。例如:ctx.font = '40px Arial'; - 试着把它放在你的小提琴中
      • 顺便说一句,Words(区分大小写)变量到底是从哪里来的?它没有在任何地方定义。这部分代码只有在您更改字体时才会执行..
      • @psychobrm 你完全正确。这是一个错误(我已经修复了它)。仅当您必须将一个单词分成两行时才执行这部分代码。谢谢!
      • 我进行了一些我需要的升级:渲染空格、渲染前导/尾随换行符、渲染描边和一次调用填充(不要测量文本两次),我还必须更改迭代,因为for in 不适用于扩展 Array.prototype。你能把它放在github上,以便我们可以迭代它吗?
      • @psychobrm 我合并了您的更改。谢谢!
      【解决方案7】:

      我在这里为这个场景创建了一个小型库:Canvas-Txt

      它以多行形式呈现文本,并提供不错的对齐模式。

      要使用它,您需要安装它或使用 CDN。

      安装

      npm install canvas-txt --save
      

      JavaScript

      import canvasTxt from 'canvas-txt'
      
      var c = document.getElementById('myCanvas')
      var ctx = c.getContext('2d')
      
      var txt = 'Lorem ipsum dolor sit amet'
      
      canvasTxt.fontSize = 24
      
      canvasTxt.drawText(ctx, txt, 100, 200, 200, 200)
      

      这将在一个不可见的框中呈现文本,其位置/尺寸为:

      { x: 100, y: 200, height: 200, width: 200 }
      

      小提琴示例

      /* https://github.com/geongeorge/Canvas-Txt  */
      
      const canvasTxt = window.canvasTxt.default;
      const ctx = document.getElementById('myCanvas').getContext('2d');
      
      const txt = "Lorem ipsum dolor sit amet";
      const bounds = { width: 240, height: 80 };
      
      let origin = { x: ctx.canvas.width / 2, y: ctx.canvas.height / 2, };
      let offset = { x: origin.x - (bounds.width / 2), y: origin.y - (bounds.height / 2) };
      
      canvasTxt.fontSize = 20;
      
      ctx.fillStyle = '#C1A700';
      ctx.fillRect(offset.x, offset.y, bounds.width, bounds.height);
      
      ctx.fillStyle = '#FFFFFF';
      canvasTxt.drawText(ctx, txt, offset.x, offset.y, bounds.width, bounds.height);
      body {
        background: #111;
      }
      
      canvas {
        border: 1px solid #333;
        background: #222; /* Could alternatively be painted on the canvas */
      }
      <script src="https://unpkg.com/canvas-txt@2.0.6/build/index.js"></script>
      
      <canvas id="myCanvas" width="300" height="160"></canvas>

      【讨论】:

      • 我继续定义了一些变量来帮助“自我记录”示例。它还处理画布内的边界框居中。我还在后面添加了一个矩形,因此您实际上可以看到它相对居中。做得好! +1我注意到的一件小事是,换行的行不会抑制其前导空格。您可能想要修剪每一行,例如ctx.fillText(txtline.trim(), textanchor, txtY) 我只在您网站上的交互式演示中注意到这一点。
      • @Mr.Polywhirl 感谢您澄清答案。我已经修复了修剪问题并发布了2.0.9 版本。演示站点是通过更新包版本来修复的。多个空格存在问题。我不知道是选择一个固执己见的包裹还是忽略这个问题更好。收到来自多个地方的请求。无论如何,我继续添加修剪。 Lorem ipsum dolor, sit &lt;many spaces&gt; amet 这就是我一开始没有这样做的原因。您认为我应该考虑多个空格并仅在只有一个空格时才删除?
      • 编辑:似乎 StackOverflow 代码块也忽略了多个空格
      【解决方案8】:

      我使用 javascript 开发了一个解决方案。它并不漂亮,但对我有用:


      function drawMultilineText(){
      
          // set context and formatting   
          var context = document.getElementById("canvas").getContext('2d');
          context.font = fontStyleStr;
          context.textAlign = "center";
          context.textBaseline = "top";
          context.fillStyle = "#000000";
      
          // prepare textarea value to be drawn as multiline text.
          var textval = document.getElementByID("textarea").value;
          var textvalArr = toMultiLine(textval);
          var linespacing = 25;
          var startX = 0;
          var startY = 0;
      
          // draw each line on canvas. 
          for(var i = 0; i < textvalArr.length; i++){
              context.fillText(textvalArr[i], x, y);
              y += linespacing;
          }
      }
      
      // Creates an array where the <br/> tag splits the values.
      function toMultiLine(text){
         var textArr = new Array();
         text = text.replace(/\n\r?/g, '<br/>');
         textArr = text.split("<br/>");
         return textArr;
      }
      

      希望有帮助!

      【讨论】:

      • 你好,假设我的文字是这样的那么画布中发生了什么???
      • 它将离开画布,因为@Ramirez 没有将 maxWidth 参数放入 fillText :)
      【解决方案9】:

      @Gaby Petrioli 提供的自动换行(以空格分隔)代码非常有用。 我已经扩展了他的代码以提供对换行符\n 的支持。此外,通常有边界框的尺寸很有用,所以multiMeasureText() 返回宽度和高度。

      您可以在此处查看代码:http://jsfiddle.net/jeffchan/WHgaY/76/

      【讨论】:

      • 链接过期,请把代码放在这个答案中即使你有一个有效的链接。如果 jsfiddle 关闭,这个答案就完全没用了。
      【解决方案10】:

      这是 Colin 的 wrapText() 的一个版本,它还支持 垂直居中文本context.textBaseline = 'middle'

      var wrapText = function (context, text, x, y, maxWidth, lineHeight) {
          var paragraphs = text.split("\n");
          var textLines = [];
      
          // Loop through paragraphs
          for (var p = 0; p < paragraphs.length; p++) {
              var line = "";
              var words = paragraphs[p].split(" ");
              // Loop through words
              for (var w = 0; w < words.length; w++) {
                  var testLine = line + words[w] + " ";
                  var metrics = context.measureText(testLine);
                  var testWidth = metrics.width;
                  // Make a line break if line is too long
                  if (testWidth > maxWidth) {
                      textLines.push(line.trim());
                      line = words[w] + " ";
                  }
                  else {
                      line = testLine;
                  }
              }
              textLines.push(line.trim());
          }
      
          // Move text up if centered vertically
          if (context.textBaseline === 'middle')
              y = y - ((textLines.length-1) * lineHeight) / 2;
      
          // Render text on canvas
          for (var tl = 0; tl < textLines.length; tl++) {
              context.fillText(textLines[tl], x, y);
              y += lineHeight;
          }
      };
      

      【讨论】:

        【解决方案11】:

        如果您只需要两行文本,您可以将它们拆分为两个不同的 fillText 调用,并为每个调用赋予不同的基线。

        ctx.textBaseline="bottom";
        ctx.fillText("First line", x-position, y-position);
        ctx.textBaseline="top";
        ctx.fillText("Second line", x-position, y-position);
        

        【讨论】:

          【解决方案12】:

          这个问题并没有考虑画布的工作原理。如果您想要换行,只需调整下一个 ctx.fillText 的坐标即可。

          ctx.fillText("line1", w,x,y,z)
          ctx.fillText("line2", w,x,y,z+20)
          

          【讨论】:

            【解决方案13】:

            我认为你仍然可以依赖 CSS

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

            幸运的是,通过 CSS hack-ardry(有关修复使用 CSS 测量的旧实现的更多方法,请参阅 Typographic Metrics),我们可以通过测量具有相同字体属性的 a 的 offsetHeight 来找到文本的高度:

            var d = document.createElement(”span”);
            d.font = “20px arial”
            d.textContent = “Hello world!”
            var emHeight = d.offsetHeight;
            

            来自: http://www.html5rocks.com/en/tutorials/canvas/texteffects/

            【讨论】:

            • 如果您有足够的内存在每次需要测量时构建这样的元素,那将是一个很好的解决方案。你也可以ctx.save() 然后ctx.font = '12pt Arial' 然后parseInt( ctx.font, 10 )。请注意,我在设置时使用了“pt”。然后它将转换为 PX 并能够转换为数字以用作字体的高度。
            【解决方案14】:

            我认为这也不可能,但解决方法是创建一个 &lt;p&gt;element 并使用 Javascript 定位它。

            【讨论】:

            • 是的,这就是我想做的事情。只是有了fillText()strokeText(),你可以做超出CSS 可以做的事情。
            • 我没有对此进行测试,但我认为这可能是一个更好的解决方案——这里使用 fillText() 的其他解决方案使其无法选择(或可能粘贴)文本。跨度>
            【解决方案15】:

            由于遇到同样的问题,我遇到了这个问题。我正在使用可变字体大小,因此考虑到这一点:

            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 是用户编辑的 contenteditable div(这是嵌套在一个 jQuery 每个函数中),而 ctx.font 是“14px Arial”(注意像素大小排在第一位)

            【讨论】:

              【解决方案16】:

              这是我在画布中绘制多行文本中心的功能(只换行,不换字)

              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)
                  }
              }
              &lt;canvas id="myCanvas" width="300" height="150" style="border:1px solid #000;"&gt;&lt;/canvas&gt;

              如果你想让文本大小适合画布,你也可以检查here

              【讨论】:

                【解决方案17】:

                有一个维护良好的 JS 库,名为 Canvas-Txt,它能够处理换行和文本换行。我发现它比这个线程中的所有随机代码 sn-ps 更有用。

                【讨论】:

                  【解决方案18】:

                  Canvas 元素不支持换行符 '\n'、制表符 '\t' 或
                  标签等字符。

                  试试看:

                  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); 
                  }  
                  

                  【讨论】:

                    【解决方案19】:

                    我的 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
                      }
                    }
                    

                    有关该问题的更多信息是on my blog

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-09-10
                      • 1970-01-01
                      • 1970-01-01
                      • 2016-12-19
                      • 1970-01-01
                      相关资源
                      最近更新 更多