HTML5 полотно ctx.fillText не зробить розривів рядків?


108

Здається, я не можу додати текст до полотна, якщо текст включає "\ n". Я маю на увазі, перерив рядків не відображається / працює.

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

Вищеописаний код буде намальовано "s ome \n <br/> thing"в одному рядку.

Це обмеження fillText чи я роблю це неправильно? "\ n" є і не друкуються, але вони також не працюють.


1
чи бажаєте ви автоматично обгортати, досягаючи кінця? чи просто взяти до уваги нові рядки, присутні в тексті?
Габріеле Петріолі

Згорніть текст у кілька рядків.
Вежа

Привіт twodordan, чи існує це обмеження як для хрому, так і для мозили? Люди часто використовують простий HTML-текст, який вони наносять на полотно з позицією: абсолютна, наприклад. Крім того, ви можете зробити два fillText і перемістити Y-початковий текст вашого другого рядка.
Тім


TL; DR: або зателефонуйте fillText()кілька разів і використовуйте висоту шрифту для розділення або використовуйте developer.mozilla.org/en-US/docs/Web/API/TextMetrics developer.mozilla.org/en-US/docs/Web/API /… - або скористайтеся одним із дуже складних «рішень» нижче, які не використовують TextMetrics ...
Андрій

Відповіді:


62

Боюся, це обмеження полотна fillText. Немає багатолінійної підтримки. Що ще гірше, немає вбудованого способу вимірювання висоти лінії, лише ширина, що робить це самостійно ще складніше!

Дуже багато людей написали власну багатолінійну підтримку, мабуть, найпомітніший проект - Mozilla Skywriter .

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


Дякую! У мене було відчуття, що це буде нудно ... Приємно знати про SKYWRITER, але я просто "зачекаю", поки fillText () покращиться. У моєму випадку це було не дуже важливо. Га, немає висоти лінії, це як би хтось це робив навмисно. : D
Spectraljump

18
Чесно кажучи, я б не затримував дихання, коли fillText () "вдосконалюється", щоб підтримати це, оскільки я відчуваю, що це призначено для використання (багаторазові дзвінки та самостійний розрахунок yOffset). Я думаю, що велика потужність API для полотна полягає в тому, що він відокремлює функціональність малювання нижнього рівня від того, що ви вже можете зробити (виконайте необхідні вимірювання). Крім того, ви можете знати висоту тексту, просто надавши розмір тексту у пікселях; іншими словами: context.font = "16px Arial"; - у вас там висота; ширина - єдина, яка динамічна.
Лев

1
measureText()Додано деякі додаткові властивості для , які, на мою думку, можуть вирішити проблему. У Chrome є прапор, щоб увімкнути їх, але інші веб-переглядачі ще ... ще!
SWdV

@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

Якщо вам потрібен довгий виправданий текст, навіщо б ви використовували полотно?
Майк 'Помакс' Камерманс

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, якщо ви використовували шрифт на якомусь тексті PRIOR для маніпулювання полотном - інакше полотно не підбере шрифт.

Сподіваюся, що це комусь допоможе.


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

Uncaught ReferenceError: Words is not definedЯкщо я спробую змінити шрифт. Наприклад: ctx.font = '40px Arial';- спробуйте
помітити

Btw, звідки, пекло, змінюється 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/


Посилання закінчуються, будь ласка, введіть код у цій відповіді, навіть якщо у вас є робоче посилання. Якщо jsfiddle вимкнеться, ця відповідь стає цілком марною, як є.
Майк 'Помакс' Камерманс

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), ми зможемо знайти висоту тексту за допомогою вимірювання зсувуВисота а з тими ж властивостями шрифту:

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 і зможе перетворитись на цифру для споживання як висоту шрифту.
Ерік Ходонський

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 }

Приклад 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>


Я пішов вперед і визначив деякі змінні, щоб допомогти "самодокументувати" приклад. Він також обробляє центрування обмежувальної коробки всередині полотна. Я також додав прямокутник позаду, так що ви можете бачити його по центру по відношенню. Чудова робота! +1 Одне невелике, що я помітив, - це те, що лінії, які обгортають, не будуть придушені провідними пробілами. Ви можете обрізати кожен рядок, наприклад, це ctx.fillText(txtline.trim(), textanchor, txtY)я помітив лише у вашій інтерактивній демонстрації на вашому веб-сайті.
Містер Polywhirl

@ Mr.Polywhirl Дякую, що прояснили відповідь. Я виправив обробку і опублікував 2.0.9версію. Демо-сайт виправлено шляхом оновлення версії пакета. Виникає проблема з кількома пробілами. Я не знаю, чи краще їхати з продуманим пакетом чи ігнорувати проблему. Надходили запити на це з різних місць. Я пішов вперед і все одно додав обробку. Lorem ipsum dolor, sit <many spaces> amet це було причиною, чому я цього не зробив в першу чергу. Як ви думаєте, чи слід вважати кілька пробілів і видаляти їх лише у тому випадку, якщо є лише один?
Геон Джордж

Редагувати: схоже, блок коду StackOverflow також ігнорує кілька пробілів
Geon George

2

Я також не думаю, що це неможливо, але вирішення цього завдання - створити <p>елемент і розмістити його за допомогою Javascript.


Так, саме так я і думаю. Це просто так, fillText()і з strokeText(), ви можете робити речі, що перевищують CSS.
Вежа

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


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

Більше інформації про це є в моєму блозі .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.