HTML5 canvas ctx.fillText sẽ không ngắt dòng?


108

Tôi dường như không thể thêm văn bản vào canvas nếu văn bản bao gồm "\ n". Ý tôi là, các dấu ngắt dòng không hiển thị / hoạt động.

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

Đoạn mã trên sẽ vẽ "s ome \n <br/> thing"trên một dòng.

Đây có phải là một hạn chế của fillText hay tôi làm sai? "\ n" ở đó, và không được in, nhưng chúng cũng không hoạt động.


1
bạn có muốn tự động quấn khi đến cuối? hay chỉ để xem xét các ký tự dòng mới có trong văn bản?
Gabriele Petrioli

Gói văn bản thành nhiều dòng.
Tháp

Xin chào twodordan, hạn chế này có tồn tại trên cả chrome và mozilla không? Mọi người thường sử dụng văn bản html đơn giản mà họ đặt trên canvas với vị trí: ví dụ: tuyệt đối. Ngoài ra, bạn có thể thực hiện hai fillText và di chuyển điểm gốc Y của văn bản cho các dòng thứ hai.
Tim

Bản sao HTML5 Canvas
MvG

TL; DR: Gọi fillText()nhiều lần và sử dụng chiều cao phông chữ của bạn để phân tách hoặc sử dụng developer.mozilla.org/en-US/docs/Web/API/TextMetrics developer.mozilla.org/en-US/docs/Web/API /… - hoặc, sử dụng một trong những "giải pháp" rất phức tạp bên dưới không sử dụng TextMetrics ...
Andrew

Câu trả lời:


62

Tôi e rằng đó là một hạn chế của Canvas ' fillText. Không có hỗ trợ nhiều dòng. Tệ hơn nữa, không có cách tích hợp nào để đo chiều cao dòng, chỉ đo chiều rộng, khiến bạn tự làm việc đó thậm chí còn khó hơn!

Rất nhiều người đã viết hỗ trợ nhiều dòng của riêng họ, có lẽ dự án đáng chú ý nhất là Mozilla Skywriter .

Ý chính của những gì bạn cần làm là nhiều fillTextlệnh gọi trong khi mỗi lần thêm chiều cao của văn bản vào giá trị y. (Tôi tin là đo chiều rộng của M là cách mà người viết skywriter làm để gần đúng văn bản.)


Cảm ơn bạn! Tôi có cảm giác sẽ rất phiền phức ... Rất vui khi biết về SKYWRITER, nhưng tôi sẽ chỉ "đợi" cho đến khi fillText () được cải thiện. Đó không phải là một thỏa thuận quá quan trọng trong trường hợp của tôi. Hah, không có chiều cao dòng, giống như ai đó đã cố tình làm vậy. : D
Spectraljump

18
Thành thật mà nói, tôi sẽ không nín thở khi fillText () được "cải thiện" để hỗ trợ điều này, vì tôi có cảm giác đây là cách nó được dự định sử dụng (nhiều cuộc gọi và tự tính yOffset). Tôi nghĩ phần lớn sức mạnh của canvas API là nó tách chức năng vẽ cấp thấp hơn với những gì bạn đã có thể làm (thực hiện các phép đo cần thiết). Ngoài ra, bạn có thể biết chiều cao văn bản đơn giản bằng cách cung cấp kích thước văn bản theo pixel; nói cách khác: context.font = "16px Arial"; - bạn có chiều cao ở đó; chiều rộng là chiều duy nhất là động.
Lev

1
Một số thuộc tính bổ sung cho measureText()đã được thêm vào mà tôi nghĩ có thể giải quyết vấn đề. Chrome có cờ để kích hoạt chúng, nhưng các trình duyệt khác thì chưa ...!
SWdV

@SWdV chỉ để rõ ràng, những thứ đó đã có trong thông số kỹ thuật trong nhiều năm rồi, có thể còn nhiều năm nữa cho đến khi chúng ta có đủ rộng rãi để sử dụng :(
Simon Sarris

67

Nếu bạn chỉ muốn quan tâm đến các ký tự dòng mới trong văn bản, bạn có thể mô phỏng nó bằng cách tách văn bản ở các dòng mới và gọi nhiều lần fillText()

Một cái gì đó giống như 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>


Tôi vừa tạo một ví dụ về khái niệm bao bọc ( bao bọc tuyệt đối ở độ rộng được chỉ định. Chưa xử lý các từ bị đứt đoạn )
tại 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>


Và một bằng chứng về khái niệm gói từ ( ngắt ở khoảng cách ).
ví dụ tại 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>


Trong ví dụ thứ hai và thứ ba, tôi đang sử dụng measureText()phương pháp cho biết độ dài (tính bằng pixel ) của một chuỗi khi được in ra.


làm thế nào để biện minh cho toàn bộ văn bản dài?
Amirhossein Tarmast

Nếu bạn cần một văn bản dài, hợp lý, tại sao bạn lại sử dụng canvas?
Mike 'Pomax' Kamermans

39

Có thể đến bữa tiệc này hơi muộn, nhưng tôi thấy hướng dẫn gói văn bản trên canvas sau đây thật hoàn hảo.

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

Từ đó tôi có thể nghĩ rằng làm cho nhiều dòng hoạt động (xin lỗi Ramirez, của bạn không hiệu quả với tôi!). Mã hoàn chỉnh của tôi để bọc văn bản trong canvas như sau:

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

Đâu clà ID của canvas của tôi và textlà ID của hộp văn bản của tôi.

Như bạn có thể thấy đang sử dụng một phông chữ không chuẩn. Bạn có thể sử dụng @ font-face miễn là bạn đã sử dụng phông chữ trên một số văn bản TRƯỚC KHI thao tác trên canvas - nếu không canvas sẽ không chọn phông chữ.

Hy vọng điều này sẽ giúp ai đó.


26

Chia văn bản thành các dòng và vẽ từng dòng riêng biệt:

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

17

Đây là giải pháp của tôi, sửa đổi hàm wrapText () phổ biến đã được trình bày ở đây. Tôi đang sử dụng tính năng tạo mẫu của JavaScript để bạn có thể gọi hàm từ ngữ cảnh canvas.

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

Cách sử dụng cơ bản:

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

Đây là một minh chứng mà tôi tổng hợp lại: http://jsfiddle.net/7RdbL/


Làm việc như người ở. Cảm ơn bạn.
couzzi

13

Tôi vừa mở rộng CanvasRenderingContext2D thêm hai chức năng: mlFillText và mlStrokeText.

Bạn có thể tìm thấy phiên bản cuối cùng trong GitHub :

Với các chức năng này, bạn có thể điền / đột quỵ văn bản tiêu đề trong một hộp. Bạn có thể căn chỉnh văn bản theo chiều dọc và chiều ngang. (Nó tính đến tài khoản \ n và cũng có thể điều chỉnh văn bản).

Các nguyên mẫu là:

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

Trong đó vAlign có thể là: "top", "center" hoặc "button" Và hAlign có thể là: "left", "center", "right" hoặc "justify"

Bạn có thể kiểm tra lib tại đây: http://jsfiddle.net/4WRZj/1/

nhập mô tả hình ảnh ở đây

Đây là mã của thư viện:

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

Và đây là ví dụ sử dụng:

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 definedNếu tôi cố gắng thay đổi phông chữ. Ví dụ: ctx.font = '40px Arial';- thử đặt rằng trong fiddle của bạn
BRM tâm lý

Btw, biến Words(phân biệt chữ hoa chữ thường) đến từ đâu ?? Nó không được định nghĩa ở bất cứ đâu. Đó là một phần của mã chỉ được thực hiện khi bạn thay đổi phông chữ ..
BRM tâm lý

1
@psychobrm Bạn hoàn toàn đúng. Đó là một lỗi (tôi đã sửa nó). Phần mã này chỉ được thực thi nếu bạn phải chia một từ thành hai dòng. Cảm ơn bạn!
jbaylina

Tôi đã thực hiện một số nâng cấp mà tôi cần: kết xuất khoảng trắng, hiển thị dòng mới ở đầu / cuối, hiển thị nét vẽ và điền vào một lệnh gọi (không đo văn bản hai lần), tôi cũng đã phải thay đổi lặp lại, vì for inkhông hoạt động tốt với mở rộng Array.prototype. Bạn có thể đặt nó trên github để chúng tôi có thể lặp lại trên đó không?
tâm lý brm

@psychobrm Tôi đã hợp nhất các thay đổi của bạn. Cảm ơn bạn!
jbaylina

8

Sử dụng javascript, tôi đã phát triển một giải pháp. Nó không đẹp nhưng nó phù hợp với tôi:


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

Hy vọng rằng sẽ giúp!


1
xin chào, giả sử văn bản của tôi giống như thế này var text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa sau đó điều gì đang xảy ra trong canvas ???
Amol Navsupe

Nó sẽ đi ra khỏi vải, như @Ramirez không đặt tham số maxWidth để fillText :)
KaHa6uc

6

Mã gói từ (ngắt ở dấu cách) do @Gaby Petrioli cung cấp rất hữu ích. Tôi đã mở rộng mã của anh ấy để cung cấp hỗ trợ cho các ký tự dòng mới \n. Ngoài ra, thường rất hữu ích khi có kích thước của hộp giới hạn, vì vậy multiMeasureText()trả về cả chiều rộng và chiều cao.

Bạn có thể xem mã tại đây: http://jsfiddle.net/jeffchan/WHgaY/76/


liên kết hết hạn, vui lòng đặt mã trong câu trả lời này ngay cả khi bạn có liên kết đang hoạt động. Nếu jsfiddle tắt, câu trả lời này sẽ trở nên hoàn toàn vô dụng.
Mike 'Pomax' Kamermans

5

Đây là phiên bản của Colin wrapText()cũng hỗ trợ văn bản căn giữa theo chiều dọc với 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;
    }
};

5

Nếu bạn chỉ cần hai dòng văn bản, bạn có thể chia chúng thành hai lệnh gọi fillText khác nhau và cung cấp cho mỗi dòng một đường cơ sở khác nhau.

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

4

Câu hỏi này không liên quan đến cách hoạt động của canvas. Nếu bạn muốn ngắt dòng, chỉ cần điều chỉnh tọa độ tiếp theo của bạn ctx.fillText.

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

3

Tôi nghĩ bạn vẫn có thể dựa vào CSS

ctx.measureText().height doesnt exist.

May mắn thay, thông qua CSS hack-ardry (xemTypographic Metrics để biết thêm cách khắc phục các cách triển khai cũ hơn bằng cách sử dụng các phép đo CSS), chúng ta có thể tìm thấy chiều cao của văn bản thông qua việc đo offsetHeight của a với cùng một thuộc tính phông chữ:

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

từ: http://www.html5rocks.com/en/tutorials/canvas/texteffects/


Đó là một giải pháp tốt nếu bạn có trí nhớ để xây dựng một phần tử như vậy mỗi khi bạn cần đo lường. Bạn cũng có thể ctx.save()sau đó, ctx.font = '12pt Arial' sau đó, parseInt( ctx.font, 10 ). Lưu ý rằng tôi sử dụng 'pt' khi đặt nó. Sau đó, nó sẽ chuyển thành PX và có thể chuyển thành một chữ số để tiêu dùng làm chiều cao của phông chữ.
Eric Hodonsky

3

Tôi đã tạo một thư viện nhỏ cho tình huống này ở đây: Canvas-Txt

Nó hiển thị văn bản trong nhiều dòng và nó cung cấp các chế độ căn chỉnh phù hợp.

Để sử dụng, bạn cần cài đặt hoặc sử dụng CDN.

Cài đặt

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)

Điều này sẽ hiển thị văn bản trong một hộp vô hình với vị trí / kích thước là:

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

Ví dụ 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>


Tôi đã đi trước và xác định một số biến để giúp "tự ghi lại" ví dụ. Nó cũng xử lý căn giữa hộp giới hạn trong canvas. Tôi cũng đã thêm một hình chữ nhật phía sau, vì vậy bạn thực sự có thể thấy nó được căn giữa theo mối quan hệ. Công việc tuyệt vời! +1 Một điều nhỏ tôi nhận thấy là các dòng bọc sẽ không có khoảng trống ở đầu của chúng bị triệt tiêu. Bạn có thể muốn cắt từng dòng, ví dụ như ctx.fillText(txtline.trim(), textanchor, txtY)tôi chỉ nhận thấy điều này trong bản demo tương tác trên trang web của bạn.
Ông Polywhirl

@ Mr.Polywhirl Cảm ơn bạn đã giải đáp thắc mắc. Tôi đã khắc phục sự cố cắt và xuất 2.0.9bản phiên bản. Trang web demo được sửa bằng cách cập nhật phiên bản gói. Có vấn đề với nhiều khoảng trắng. Tôi không biết liệu tốt hơn là nên đi với một gói cố định hay bỏ qua vấn đề. Đã nhận được yêu cầu cho điều này từ nhiều nơi. Tôi đã tiếp tục và thêm bớt phần nào. Lorem ipsum dolor, sit <many spaces> amet đây là lý do tại sao tôi không làm điều đó ngay từ đầu. Bạn nghĩ sao tôi nên xem xét nhiều khoảng trắng và chỉ xóa nếu chỉ có một?
Geon George

Chỉnh sửa: có vẻ như khối mã StackOverflow cũng bỏ qua nhiều khoảng trắng
Geon George

2

Tôi không nghĩ rằng điều này là không thể, nhưng một giải pháp cho điều này là tạo một <p>phần tử và định vị nó bằng Javascript.


Vâng, đó là những gì tôi đang nghĩ đến việc làm. Chỉ là với fillText()strokeText(), bạn có thể làm những điều ngoài những gì CSS có thể làm.
Tháp

Tôi chưa thử nghiệm điều này, nhưng tôi nghĩ đây có thể là một giải pháp tốt hơn - các giải pháp khác ở đây bằng cách sử dụng fillText () làm cho nó không thể chọn văn bản (hoặc có lẽ đã được dán).
Jerry Asher

2

Tôi đã xảy ra điều này do có cùng một vấn đề. Tôi đang làm việc với kích thước phông chữ thay đổi, vì vậy điều này có tính đến:

var texts=($(this).find('.noteContent').html()).split("<br>");
for (var k in texts) {
    ctx.fillText(texts[k], left, (top+((parseInt(ctx.font)+2)*k)));
}

trong đó .noteContent là div có thể chỉnh sửa nội dung mà người dùng đã chỉnh sửa (cái này được lồng trong jQuery từng hàm) và ctx.font là "14px Arial" (lưu ý rằng kích thước pixel đứng trước)


0

Phần tử canvas không hỗ trợ các ký tự như dòng mới '\ n', tab '\ t' hoặc thẻ <br />.

Thử nó:

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 

hoặc có thể nhiều dòng:

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

0

Giải pháp ES5 của tôi cho vấn đề:

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

Thông tin thêm về vấn đề này có trên blog của tôi .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.