HTML5 Canvas ctx.fillText не будет переносить строки?

108

Кажется, я не могу добавить текст на холст, если текст содержит «\ n». То есть, разрывы строк не отображаются / работают.

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

Приведенный выше код будет рисоваться "s ome \n <br/> thing"в одной строке.

Это ограничение fillText или я ошибаюсь? символы "\ n" есть и не печатаются, но они тоже не работают.

Spectraljump
источник
1
вы хотите автоматически переносить по достижении конца? или просто принять во внимание символы новой строки, присутствующие в тексте?
Габриэле Петриоли
Разбейте текст на несколько строк.
Башня
Привет, twodordan, существует ли это ограничение как для Chrome, так и для Mozilla? Люди часто используют простой html-текст, который они помещают на холст с положением, например, absolute. Также вы можете сделать два fillText и переместить начало Y вашего текста для ваших вторых строк.
Тим
TL; DR: либо вызовите fillText()несколько раз и используйте высоту шрифта для разделения, либо используйте developer.mozilla.org/en-US/docs/Web/API/TextMetrics developer.mozilla.org/en-US/docs/Web/API /… - или используйте одно из очень сложных «решений» ниже, которое не использует TextMetrics ...
Эндрю

Ответы:

62

Боюсь, это ограничение Canvas fillText. Многострочной поддержки нет. Что еще хуже, нет встроенного способа измерить высоту строки, только ширину, что делает это самостоятельно еще сложнее!

Многие люди написали свою собственную многострочную поддержку, возможно, самый известный проект - Mozilla Skywriter .

Суть того, что вам нужно сделать, - это несколько fillTextвызовов, каждый раз добавляя высоту текста к значению y. (Я считаю, что люди, пишущие на небе, измеряют ширину буквы M, чтобы приблизить текст.)

Саймон Саррис
источник
Спасибо! У меня было чувство, что это будет утомительно ... Приятно узнать о SKYWRITER, но я просто «подожду», пока fillText () не улучшится. В моем случае это не было особо важной сделкой. Ха, без высоты строки, это как будто специально это сделали. : D
Spectraljump
19
Честно говоря, я бы не стал задерживать дыхание на «улучшении» fillText () для поддержки этого, поскольку у меня такое ощущение, что он предназначен для использования именно так (несколько вызовов и самостоятельное вычисление yOffset). Я думаю, что большая часть возможностей Canvas API заключается в том, что он отделяет функциональность рисования нижнего уровня от того, что вы уже можете делать (выполнять необходимые измерения). Кроме того, вы можете узнать высоту текста, просто указав размер текста в пикселях; другими словами: context.font = "16px Arial"; - у вас там высота; ширина - единственная динамическая.
Лев
1
Были добавлены некоторые дополнительные свойства для measureText(), которые, я думаю, могут решить проблему. В Chrome есть флаг для их включения, но в других браузерах его нет ... пока!
SWdV 07
@SWdV просто для ясности, они были в спецификации уже много лет, могут пройти годы, пока мы не получим достаточно широкого распространения, чтобы использовать :(
Саймон Саррис
67

Если вы просто хотите позаботиться о символах новой строки в тексте, вы можете смоделировать это, разделив текст на новые строки и несколько раз вызывая 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()метод, который показывает, какой длины ( в пикселях ) будет строка при печати.

Габриэле Петриоли
источник
как выровнять весь длинный текст?
Amirhossein Tarmast
Если вам нужен длинный, выровненный по ширине текст, зачем вам холст?
Майк «Pomax» Камерманс,
39

Может быть, прийти на эту вечеринку немного поздно, но я нашел следующий урок по переносу текста на холст идеальным.

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идентификатор моего холста и textидентификатор моего текстового поля.

Как видите, я использую нестандартный шрифт. Вы можете использовать @ font-face, если вы использовали шрифт в некотором тексте ДО манипулирования холстом - иначе холст не подберет шрифт.

Надеюсь, это кому-то поможет.

Колин Вайзман
источник
26

Разделите текст на строки и нарисуйте каждую отдельно:

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

Вот мое решение, изменяющее популярную функцию 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/

Джейк
источник
Работал как шарм. Спасибо.
couzzi
13

Я просто расширил CanvasRenderingContext2D, добавив две функции: mlFillText и mlStrokeText.

Вы можете найти последнюю версию в GitHub :

С помощью этой функции вы можете заливать / обводить многострочный текст в поле. Вы можете выровнять текст по вертикали и горизонтали. (Он учитывает \ n, а также может выравнивать текст).

Прототипы:

функция mlFillText (текст, x, y, w, h, vAlign, hAlign, lineheight); функция mlStrokeText (текст, x, y, w, h, vAlign, hAlign, lineheight);

Где vAlign может быть: «вверху», «по центру» или «кнопка», а hAlign может быть: «влево», «по центру», «вправо» или «выравнивать по ширине».

Вы можете протестировать библиотеку здесь: 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);
jbaylina
источник
Uncaught ReferenceError: Words is not definedЕсли я попытаюсь изменить шрифт. Например: ctx.font = '40px Arial';- попробуйте вставить это в свою скрипку
psycho brm
Кстати, откуда, черт возьми, Wordsпеременная (чувствительная к регистру) ?? Это нигде не определено. Эта часть кода выполняется только тогда, когда вы меняете шрифт ..
psycho brm
1
@psychobrm Вы абсолютно правы. Это ошибка (я уже исправил). Эта часть кода выполняется только в том случае, если вам нужно разбить слово на две строки. Спасибо!
jbaylina
Я сделал несколько необходимых мне обновлений: рендеринг пробелов, рендеринг начальных / конечных символов новой строки, рендеринга обводки и заливки одним вызовом (не измеряйте текст дважды), мне также пришлось изменить итерацию, поскольку for inне работает с расширенным Array.prototype. Не могли бы вы поместить его на github, чтобы мы могли его повторить?
psycho brm
@psychobrm Я объединил ваши изменения. Спасибо!
jbaylina
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;
}

Надеюсь, это поможет!

Рамирес
источник
1
привет, предположим , что мой текст как этот вар текст = «aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa»; тогда что случилось с холстом ???
Амол Навсупе
Он выйдет за пределы холста, поскольку @Ramirez не поместил параметр maxWidth в fillText :)
KaHa6uc
6

Код для переноса слов (разрыв пробелов), предоставленный @Gaby Petrioli , очень полезен. Я расширил его код, чтобы обеспечить поддержку символов новой строки \n. Кроме того, часто бывает полезно иметь размеры ограничивающей рамки, поэтому multiMeasureText()возвращаются и ширина, и высота.

Вы можете увидеть код здесь: http://jsfiddle.net/jeffchan/WHgaY/76/

Jeffchan
источник
срок действия ссылок истекает, введите код в этот ответ, даже если у вас есть рабочая ссылка. Если jsfiddle закрывается, этот ответ становится совершенно бесполезным.
Майк «Pomax» Камерманс,
5

Вот версия Колина , 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;
    }
};
Том Сёдерлунд
источник
5

Если вам нужны только две строки текста, вы можете разделить их на два разных вызова fillText и присвоить каждому отдельную базовую линию.

ctx.textBaseline="bottom";
ctx.fillText("First line", x-position, y-position);
ctx.textBaseline="top";
ctx.fillText("Second line", x-position, y-position);
вещь
источник
4

Этот вопрос не связан с принципами работы холста. Если вам нужен разрыв строки, просто измените координаты следующего ctx.fillText.

ctx.fillText("line1", w,x,y,z)
ctx.fillText("line2", w,x,y,z+20)
Джейджей
источник
3

Я думаю, что вы все еще можете положиться на CSS

ctx.measureText().height doesnt exist.

К счастью, с помощью хитрости CSS (см. «Типографические метрики», чтобы узнать о других способах исправления старых реализаций использования измерений CSS), мы можем найти высоту текста, измерив 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/

MarioF
источник
Это хорошее решение, если у вас есть память для создания такого элемента каждый раз, когда вам нужно проводить измерения. Вы можете также ctx.save()тогда, ctx.font = '12pt Arial' то, parseInt( ctx.font, 10 ). Обратите внимание, что я использую pt при настройке. Затем он будет переведен в PX и сможет превращаться в цифру для использования в качестве высоты шрифта.
Эрик Годонски
3

Я создал крошечную библиотеку для этого сценария здесь: 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 <many spaces> amet это была причина, по которой я этого не делал. Как вы думаете, следует ли рассматривать несколько пробелов и удалять только в том случае, если есть только один?
Джеон Джордж,
Изменить: кажется, что блок кода StackOverflow также игнорирует несколько пробелов
Геон Джордж
2

Я тоже не думаю, что это возможно, но обходной путь - создать <p>элемент и разместить его с помощью Javascript.

Хармен
источник
Да, это то, что я собираюсь сделать. Просто с помощью fillText()и strokeText()вы можете делать вещи, выходящие за рамки возможностей CSS.
Tower
Я не тестировал это, но думаю, что это может быть лучшим решением - другие решения здесь, использующие fillText (), делают так, чтобы текст не мог быть выбран (или предположительно вставлен).
Джерри Ашер
2

Я столкнулся с этим из-за той же проблемы. Я работаю с переменным размером шрифта, поэтому это учитывает:

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 - это контентный div, который редактировал пользователь (он вложен в каждую функцию jQuery), а ctx.font - "14px Arial" (обратите внимание, что размер пикселя идет первым)

MaKR
источник
0

Элемент Canvas не поддерживает такие символы, как новая строка '\ n', табуляция '\ t' или тег <br />.

Попытайся:

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); 
}  
Дариуш Дж.
источник
0

Мое решение проблемы 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
  }
}

Подробнее об этом в моем блоге .

Олег Берман
источник