HTML5 canvas ctx.fillText won't do line breaks?

前端 未结 17 1037
甜味超标
甜味超标 2020-11-28 01:37

I can\'t seem to be able to add text to a canvas if the text includes \"\\n\". I mean, the line breaks do not show/work.

ctxPaint.fillText(\"s  ome \\n \\\\n         


        
相关标签:
17条回答
  • 2020-11-28 02:14

    If you only need two lines of text, you can split them into two different fillText calls and give each one a different baseline.

    ctx.textBaseline="bottom";
    ctx.fillText("First line", x-position, y-position);
    ctx.textBaseline="top";
    ctx.fillText("Second line", x-position, y-position);
    
    0 讨论(0)
  • 2020-11-28 02:18

    Maybe coming to this party a bit late, but I found the following tutorial for wrapping text on a canvas perfect.

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

    From that I was able to think get multi lines working (sorry Ramirez, yours didn't work for me!). My complete code to wrap text in a canvas is as follows:

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

    Where c is the ID of my canvas and text is the ID of my textbox.

    As you can probably see am using a non-standard font. You can use @font-face as long as you have used the font on some text PRIOR to manipulating the canvas - otherwise the canvas won't pick up the font.

    Hope this helps someone.

    0 讨论(0)
  • 2020-11-28 02:18

    The code for word-wrapping (breaking at spaces) provided by @Gaby Petrioli is very helpful. I've extended his code to provide support for newline characters \n. Also, often times it's useful to have the dimensions of the bounding box, so multiMeasureText() returns both the width and the height.

    You can see the code here: http://jsfiddle.net/jeffchan/WHgaY/76/

    0 讨论(0)
  • 2020-11-28 02:22

    I just extended the CanvasRenderingContext2D adding two functions: mlFillText and mlStrokeText.

    You can find the last version in GitHub:

    With this functions you can fill / stroke miltiline text in a box. You can align the text verticaly and horizontaly. (It takes in account \n's and can also justify the text).

    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"

    You can test the lib here: http://jsfiddle.net/4WRZj/1/

    enter image description here

    Here is the code of the library:

    // 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");
        };
    })();
    

    And here is the use example:

    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);
    
    0 讨论(0)
  • 2020-11-28 02:22

    I created a tiny library for this scenario here: Canvas-Txt

    It renders text in multi-line and it offers decent alignment modes.

    In order to use this, you will need to either install it or use a CDN.

    Installation

    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)
    

    This will render text in an invisible box with position/dimensions of:

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

    Example Fiddle

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

    0 讨论(0)
  • 2020-11-28 02:23

    My ES5 solution for the problem:

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

    More information on the issue is on my blog.

    0 讨论(0)
提交回复
热议问题