HTML5 canvas ctx.fillText não faz quebras de linha?

108

Não consigo adicionar texto a uma tela se o texto inclui "\ n". Quer dizer, as quebras de linha não mostram / funcionam.

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

O código acima será desenhado "s ome \n <br/> thing"em uma linha.

Isso é uma limitação do fillText ou estou fazendo errado? os "\ n" estão lá e não são impressos, mas também não funcionam.

Spectraljump
fonte
1
deseja embrulhar automaticamente ao chegar ao fim? ou apenas para levar em consideração os caracteres de nova linha presentes no texto?
Gabriele Petrioli
Quebra o texto em várias linhas.
Torre
Olá twodordan, essa limitação existe tanto no Chrome quanto no Mozilla? As pessoas costumam usar texto html simples que colocam sobre a tela com uma posição: absoluta, por exemplo. Além disso, você pode fazer dois fillText e mover a origem Y do seu texto para as segundas linhas.
Tim
TL; DR: chame fillText()várias vezes e use a altura da fonte para separar ou use developer.mozilla.org/en-US/docs/Web/API/TextMetrics developer.mozilla.org/en-US/docs/Web/API /… - ou, use uma das "soluções" muito complicadas abaixo que não usam TextMetrics ...
Andrew

Respostas:

62

Receio que seja uma limitação do Canvas ' fillText. Não há suporte para várias linhas. O que é pior, não há uma maneira embutida de medir a altura da linha, apenas a largura, tornando ainda mais difícil fazer você mesmo!

Muitas pessoas escreveram seu próprio suporte multi-linha, talvez o projeto mais notável que tem é o Mozilla Skywriter .

A essência do que você precisa fazer são várias fillTextchamadas enquanto adiciona a altura do texto ao valor y a cada vez. (medir a largura de M é o que o pessoal do skywriter faz para aproximar o texto, acredito.)

Simon Sarris
fonte
Obrigado! Tive a sensação de que seria incômodo ... É bom saber sobre o SKYWRITER, mas vou apenas "esperar" até que fillText () seja melhorado. Não foi um negócio muito importante no meu caso. Hah, sem altura de linha, é como se alguém tivesse feito isso de propósito. : D
Spectraljump
18
Honestamente, eu não prenderia sua respiração se o fillText () fosse "aprimorado" para dar suporte a isso, pois tenho a sensação de que é assim que ele deve ser usado (várias chamadas e você mesmo calculando o yOffset). Acho que muito do poder da API do canvas é que ela separa a funcionalidade de desenho de nível inferior do que você já pode fazer (realizar as medidas necessárias). Além disso, você pode saber a altura do texto simplesmente fornecendo o tamanho do texto em pixels; em outras palavras: context.font = "16px Arial"; - você tem a altura lá; a largura é a única dinâmica.
Lev
1
Algumas propriedades adicionais para measureText()foram adicionadas, o que eu acho que podem resolver o problema. O Chrome tem um sinalizador para ativá-los, mas outros navegadores não ... ainda!
SWdV de
@SWdV só para ficar claro, eles estão na especificação há anos, pode levar anos ainda até que tenhamos uma adoção ampla o suficiente para usar :(
Simon Sarris
67

Se você apenas deseja cuidar dos caracteres de nova linha no texto, você pode simular dividindo o texto nas novas linhas e chamando várias vezes o fillText()

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


Acabei de fazer uma prova de conceito de empacotamento ( envoltório absoluto na largura especificada. Sem quebra de palavras de manipulação, ainda )
exemplo em 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>


E uma prova de conceito de quebra de linha ( quebra de espaços ).
exemplo em 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>


No segundo e terceiro exemplos, estou usando o measureText()método que mostra quanto tempo ( em pixels ) uma string terá quando impressa.

Gabriele Petrioli
fonte
como justificar todo o texto longo?
Amirhossein Tarmast
Se você precisa de um texto longo e justificado, por que usar uma tela?
Mike 'Pomax' Kamermans
39

Talvez chegue a esta festa um pouco tarde, mas achei o seguinte tutorial para embrulhar texto em uma tela perfeito.

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

A partir disso, pude pensar em fazer várias linhas funcionarem (desculpe Ramirez, a sua não funcionou para mim!) Meu código completo para quebrar o texto em uma tela é o seguinte:

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

Onde cestá o ID da minha tela e texté o ID da minha caixa de texto.

Como você provavelmente pode ver, estou usando uma fonte fora do padrão. Você pode usar @ font-face contanto que tenha usado a fonte em algum texto ANTES de manipular a tela - caso contrário, a tela não pegará a fonte.

Espero que isso ajude alguém.

Colin Wiseman
fonte
26

Divida o texto em linhas e desenhe cada uma separadamente:

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;
  }
}
Rok Strniša
fonte
17

Esta é minha solução, modificando a popular função wrapText () que já é apresentada aqui. Estou usando o recurso de prototipagem do JavaScript para que você possa chamar a função do contexto da tela.

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

Uso básico:

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

Aqui está uma demonstração que fiz: http://jsfiddle.net/7RdbL/

Jake
fonte
Funcionou como um encanto. Obrigado.
couzzi
13

Acabei de estender o CanvasRenderingContext2D adicionando duas funções: mlFillText e mlStrokeText.

Você pode encontrar a última versão no GitHub :

Com essas funções, você pode preencher / traçar texto miltiline em uma caixa. Você pode alinhar o texto verticalmente e horizontalmente. (Leva em consideração \ n's e também pode justificar o texto).

Os protótipos são:

função mlFillText (texto, x, y, w, h, vAlign, hAlign, lineheight); função mlStrokeText (texto, x, y, w, h, vAlign, hAlign, lineheight);

Onde vAlign pode ser: "top", "center" ou "button" E hAlign pode ser: "left", "center", "right" ou "justify"

Você pode testar a lib aqui: http://jsfiddle.net/4WRZj/1/

insira a descrição da imagem aqui

Aqui está o código da biblioteca:

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

E aqui está o exemplo de uso:

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
fonte
Uncaught ReferenceError: Words is not definedSe eu tentar mudar a fonte. Por exemplo: ctx.font = '40px Arial';- tente colocar isso no seu violino
psycho brm
A propósito, de onde diabos a Wordsvariável (sensível a maiúsculas e minúsculas) vem ?? Não está definido em lugar nenhum. Essa parte do código só é executada quando você altera a fonte ..
psycho brm
1
@psychobrm Você está absolutamente certo. É um bug (já consertei). Esta parte do código só é executada se você tiver que dividir uma palavra em duas linhas. Obrigado!
jbaylina
Fiz algumas atualizações de que precisava: renderizar espaços, renderizar novas linhas iniciais / finais, renderizar traço e preencher com uma chamada (não medir texto duas vezes), também tive que alterar a iteração, já for inque não funciona bem com estendido Array.prototype. Você poderia colocá-lo no github para que possamos iterar nele?
psycho brm
@psychobrm Eu uni suas alterações. Obrigado!
jbaylina
8

Usando javascript desenvolvi uma solução. Não é bonito, mas funcionou para mim:


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

Espero que ajude!

Ramirez
fonte
1
Olá, suponha que o meu texto é como esta var text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; então o que aconteceu na tela ???
Amol Navsupe
Ele sairá da tela, pois @Ramirez não colocou o parâmetro maxWidth em fillText :)
KaHa6uc
6

O código para quebra de linha (quebra de espaços) fornecido por @Gaby Petrioli é muito útil. Eu estendi seu código para fornecer suporte para caracteres de nova linha \n. Além disso, muitas vezes é útil ter as dimensões da caixa delimitadora, portanto, multiMeasureText()retorna a largura e a altura.

Você pode ver o código aqui: http://jsfiddle.net/jeffchan/WHgaY/76/

Jeffchan
fonte
os links expiram, coloque o código nesta resposta, mesmo se você tiver um link em funcionamento. Se o jsfiddle for encerrado, essa resposta se tornará totalmente inútil como está.
Mike 'Pomax' Kamermans
5

Esta é uma versão do Colin wrapText()que também oferece suporte a texto centralizado verticalmente com 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;
    }
};
Tom Söderlund
fonte
5

Se você só precisa de duas linhas de texto, pode dividi-las em duas chamadas fillText diferentes e dar a cada uma uma linha de base diferente.

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

Esta questão não está pensando em termos de como a tela funciona. Se você quiser uma quebra de linha, basta ajustar as coordenadas da próxima ctx.fillText.

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

Acho que você ainda pode contar com CSS

ctx.measureText().height doesnt exist.

Felizmente, por meio de hackear CSS (consulte Métricas tipográficas para obter mais maneiras de corrigir implementações mais antigas de usar medições CSS), podemos encontrar a altura do texto medindo o offsetHeight de um com as mesmas propriedades de fonte:

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

de: http://www.html5rocks.com/en/tutorials/canvas/texteffects/

MarioF
fonte
Essa é uma boa solução se você tiver memória para construir um elemento como esse sempre que precisar medir. Você também pode ctx.save(), em seguida, ctx.font = '12pt Arial' em seguida, parseInt( ctx.font, 10 ). Observe que eu uso 'pt' ao configurá-lo. Em seguida, será traduzido em PX e poderá se transformar em um dígito para consumo como a altura da fonte.
Eric Hodonsky de
3

Criei uma pequena biblioteca para este cenário aqui: Canvas-Txt

Ele renderiza o texto em várias linhas e oferece modos de alinhamento decentes.

Para usar isso, você precisará instalá-lo ou usar um CDN.

Instalação

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)

Isso renderizará o texto em uma caixa invisível com posição / dimensões de:

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

Exemplo de violino

/* 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/[email protected]/build/index.js"></script>

<canvas id="myCanvas" width="300" height="160"></canvas>

Geon George
fonte
Eu fui em frente e defini algumas variáveis ​​para ajudar a "autodocumentar" o exemplo. Ele também controla a centralização da caixa delimitadora na tela. Também adicionei um retângulo atrás, para que você possa realmente vê-lo centralizado em relação. Ótimo trabalho! +1 Uma pequena coisa que notei é que as linhas que se ajustam não terão seus espaços iniciais suprimidos. Você pode querer cortar cada linha, por exemplo, ctx.fillText(txtline.trim(), textanchor, txtY)eu só percebi isso na demonstração interativa do seu site.
Sr. Polywhirl
@ Mr.Polywhirl Obrigado por esclarecer a resposta. Corrigi o problema do acabamento e publiquei a 2.0.9versão. O site de demonstração é corrigido com a atualização da versão do pacote. Há um problema com vários espaços. Não sei se é melhor ir com um pacote opinativo ou ignorar o problema. Tenho recebido pedidos para isso de vários lugares. Eu fui em frente e acrescentei o corte de qualquer maneira. Lorem ipsum dolor, sit <many spaces> amet esta foi a razão pela qual eu não fiz isso em primeiro lugar. O que você acha que devo considerar vários espaços e remover apenas se houver apenas um?
Geon George
Edit: parece que o bloco de código StackOverflow ignora vários espaços também
Geon George
2

Também não acho que isso seja possível, mas uma solução alternativa para isso é criar um <p>elemento e posicioná-lo com Javascript.

Harmen
fonte
Sim, é isso que estou pensando em fazer. É só que com fillText()e strokeText(), você pode fazer coisas além do que o CSS pode fazer.
Torre
Não testei isso, mas acho que pode ser uma solução melhor - as outras soluções aqui usando fillText () fazem com que o texto não possa ser selecionado (ou presumivelmente colado).
Jerry Asher de
2

Eu me deparei com isso devido a ter o mesmo problema. Estou trabalhando com tamanho de fonte variável, então isso leva isso em consideração:

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

onde .noteContent é o div contenteditable que o usuário editou (aninhado em um jQuery em cada função) e ctx.font é "14px Arial" (observe que o tamanho do pixel vem primeiro)

MaKR
fonte
0

O elemento Canvas não oferece suporte a caracteres como nova linha '\ n', tab '\ t' ou tag <br />.

Tente:

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 

ou talvez várias linhas:

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); 
}  
Dariusz J
fonte
0

Minha solução ES5 para o problema:

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

Mais informações sobre o assunto estão no meu blog .

Oleg Berman
fonte