HTML5 canvas ctx.fillText no hace saltos de línea?

108

Parece que no puedo agregar texto a un lienzo si el texto incluye "\ n". Quiero decir, los saltos de línea no se muestran / funcionan.

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

El código anterior se dibujará "s ome \n <br/> thing"en una línea.

¿Es esta una limitación de fillText o lo estoy haciendo mal? las "\ n" están ahí y no están impresas, pero tampoco funcionan.

Salto espectral
fuente
1
¿Quieres envolver automáticamente al llegar al final? ¿O simplemente para tener en cuenta los caracteres de nueva línea presentes en el texto?
Gabriele Petrioli
Envuelve el texto en varias líneas.
Torre
Hola, twodordan, ¿existe esta limitación tanto en Chrome como en Mozilla? Las personas a menudo usan texto html simple que colocan sobre el lienzo con una posición: absoluta, por ejemplo. También puede hacer dos fillText y mover el origen Y de su texto para sus segundas líneas.
Tim
TL; DR: llame fillText()varias veces y use la altura de su fuente para separar, o use developer.mozilla.org/en-US/docs/Web/API/TextMetrics developer.mozilla.org/en-US/docs/Web/API /… - o, use una de las "soluciones" muy complicadas a continuación que no usan TextMetrics ...
Andrew

Respuestas:

62

Me temo que es una limitación de Canvas ' fillText. No hay soporte para varias líneas. Lo que es peor, no hay una forma incorporada de medir la altura de la línea, solo el ancho, ¡lo que hace que hacerlo usted mismo sea aún más difícil!

Mucha gente ha escrito su propio soporte multilínea, quizás el proyecto más notable que tiene es Mozilla Skywriter .

La esencia de lo que deberá hacer es realizar varias fillTextllamadas mientras agrega la altura del texto al valor y cada vez. (Creo que medir el ancho de M es lo que hacen las personas que escriben en el cielo para aproximar el texto).

Simón Sarris
fuente
¡Gracias! Tenía la sensación de que sería molesto ... Es bueno saber sobre SKYWRITER, pero simplemente "esperaré" hasta que se mejore fillText (). No fue un trato muy importante en mi caso. Ja, no hay altura de línea, es como si alguien lo hubiera hecho a propósito. : D
Spectraljump
18
Honestamente, no aguantaría la respiración si fillText () está "mejorado" para respaldar esto, ya que tengo la sensación de que así es como debe usarse (múltiples llamadas y calcular el yOffset usted mismo). Creo que gran parte del poder de la API de lienzo es que separa la funcionalidad de dibujo de nivel inferior de lo que ya puede hacer (realizar las medidas necesarias). Además, puede conocer la altura del texto simplemente proporcionando el tamaño del texto en píxeles; en otras palabras: context.font = "16px Arial"; - tienes la altura ahí; el ancho es el único que es dinámico.
Lev
1
Algunas propiedades adicionales para measureText()han añadido que creo que podría resolver el problema. Chrome tiene una bandera para habilitarlos, pero otros navegadores no ... ¡todavía!
SWdV
@SWdV solo para ser claros, esos han estado en la especificación durante años, pueden pasar años hasta que tengamos una adopción lo suficientemente amplia para usar :(
Simon Sarris
67

Si solo desea cuidar los caracteres de nueva línea en el texto, puede simularlo dividiendo el texto en las líneas nuevas y llamando varias veces al 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>


Acabo de hacer una prueba de concepto de envoltura ( envoltura absoluta en el ancho especificado. Aún no se pueden romper palabras de manejo )
en 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>


Y una prueba de concepto que envuelve palabras ( rompiendo espacios ).
ejemplo en 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>


En el segundo y tercer ejemplo, estoy usando el measureText()método que muestra cuánto tiempo ( en píxeles ) será una cadena cuando se imprima.

Gabriele Petrioli
fuente
¿Cómo justificar todo el texto extenso?
Amirhossein Tarmast
Si necesita un texto extenso y justificado, ¿por qué usaría un lienzo?
Mike 'Pomax' Kamermans
39

Quizás llegar a esta fiesta un poco tarde, pero el siguiente tutorial para envolver texto en un lienzo me pareció perfecto.

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

A partir de eso, pude pensar en hacer funcionar varias líneas (lo siento Ramírez, ¡la tuya no funcionó para mí!) Mi código completo para envolver texto en un lienzo es el siguiente:

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

¿Dónde cestá el ID de mi lienzo y textes el ID de mi cuadro de texto?

Como probablemente pueda ver, estoy usando una fuente no estándar. Puede usar @ font-face siempre que haya usado la fuente en algún texto ANTES de manipular el lienzo; de lo contrario, el lienzo no recogerá la fuente.

Espero que esto ayude a alguien.

Colin Wiseman
fuente
26

Divida el texto en líneas y dibuje cada una por separado:

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

Aquí está mi solución, modificando la popular función wrapText () que ya se presenta aquí. Estoy usando la función de creación de prototipos de JavaScript para que pueda llamar a la función desde el contexto del lienzo.

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

Aquí hay una demostración que armé : http://jsfiddle.net/7RdbL/

Jake
fuente
Trabajado como un encanto. Gracias.
Couzzi
13

Acabo de extender CanvasRenderingContext2D agregando dos funciones: mlFillText y mlStrokeText.

Puedes encontrar la última versión en GitHub :

Con estas funciones puede rellenar / trazar texto miltiline en un cuadro. Puede alinear el texto vertical y horizontalmente. (Tiene en cuenta \ n's y también puede justificar el texto).

Los prototipos son:

función mlFillText (texto, x, y, w, h, vAlign, hAlign, lineheight); función mlStrokeText (texto, x, y, w, h, vAlign, hAlign, lineheight);

Donde vAlign puede ser: "arriba", "centro" o "botón" y hAlign puede ser: "izquierda", "centro", "derecha" o "justificar"

Puede probar la biblioteca aquí: http://jsfiddle.net/4WRZj/1/

ingrese la descripción de la imagen aquí

Aquí está el código de la 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");
    };
})();

Y aquí está el ejemplo 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
fuente
Uncaught ReferenceError: Words is not definedSi intento cambiar la fuente. Por ejemplo: ctx.font = '40px Arial';- intente poner eso en su violín
psycho brm
Por cierto, ¿de dónde diablos Wordsviene la variable (sensible a mayúsculas y minúsculas)? No está definido en ninguna parte. Esa parte del código sólo será ejecutado cuando se cambia la fuente ..
BRM psico
1
@psychobrm Tienes toda la razón. Es un error (ya lo solucioné). Esta parte del código solo se ejecuta si tiene que dividir una palabra en dos líneas. ¡Gracias!
jbaylina
Hice algunas actualizaciones que necesitaba: renderizar espacios, renderizar nuevas líneas iniciales / finales, renderizar trazos y rellenar con una llamada (no medir el texto dos veces), también tuve que cambiar la iteración, ya for inque no funciona bien con extendido Array.prototype. ¿Podrías ponerlo en github para que podamos iterar sobre él?
psycho brm
@psychobrm Fusioné tus cambios. ¡Gracias!
jbaylina
8

Usando javascript desarrollé una solución. No es hermoso pero funcionó para mí:


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

Ramírez
fuente
1
hola, supongamos que mi texto es como este texto var = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; Entonces, ¿qué pasa en el lienzo?
Amol Navsupe
Saldrá del lienzo, ya que @Ramirez no puso el parámetro maxWidth para fillText :)
KaHa6uc
6

El código para el ajuste de palabras ( separación de espacios) proporcionado por @Gaby Petrioli es muy útil. He ampliado su código para proporcionar compatibilidad con caracteres de nueva línea \n. Además, a menudo es útil tener las dimensiones del cuadro delimitador, por lo que multiMeasureText()devuelve tanto el ancho como el alto.

Puedes ver el código aquí: http://jsfiddle.net/jeffchan/WHgaY/76/

jeffchan
fuente
los enlaces caducan, ingrese el código en esta respuesta incluso si tiene un enlace que funcione. Si jsfiddle se apaga, esta respuesta se vuelve completamente inútil tal como está.
Mike 'Pomax' Kamermans
5

Aquí hay una versión de Colin wrapText()que también admite texto centrado verticalmente con 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
fuente
5

Si solo necesita dos líneas de texto, puede dividirlas en dos llamadas fillText diferentes y dar a cada una una línea 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);
cosa cada
fuente
4

Esta pregunta no está pensada en términos de cómo funciona el lienzo. Si desea un salto de línea, simplemente ajuste las coordenadas del siguiente ctx.fillText.

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

Creo que todavía puedes confiar en CSS

ctx.measureText().height doesnt exist.

Afortunadamente, a través de CSS hack-ardry (consulte Métricas tipográficas para conocer más formas de corregir implementaciones anteriores del uso de medidas CSS), podemos encontrar la altura del texto midiendo el offsetHeight de a con las mismas propiedades de fuente:

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
fuente
Esa es una buena solución si tiene la memoria para construir un elemento como ese cada vez que necesite medir. También puede ctx.save()entonces, ctx.font = '12pt Arial' entonces parseInt( ctx.font, 10 ),. Tenga en cuenta que utilizo 'pt' al configurarlo. Luego se traducirá a PX y podrá convertirse en un dígito para consumo como la altura de la fuente.
Eric Hodonsky
3

Creé una pequeña biblioteca para este escenario aquí: Canvas-Txt

Representa el texto en varias líneas y ofrece modos de alineación decentes.

Para usar esto, necesitará instalarlo o usar un CDN.

Instalación

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)

Esto representará el texto en un cuadro invisible con la posición / dimensiones de:

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

Ejemplo de violín

/* 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
fuente
Seguí adelante y definí algunas variables para ayudar a "autodocumentar" el ejemplo. También se encarga de centrar el cuadro delimitador dentro del lienzo. También agregué un rectángulo detrás, para que puedas verlo centrado en relación. ¡Buen trabajo! +1 Una pequeña cosa que noté es que las líneas que se ajustan no tendrán sus espacios iniciales suprimidos. Es posible que desee recortar cada línea, por ejemplo ctx.fillText(txtline.trim(), textanchor, txtY), solo noté esto en su demostración interactiva en su sitio web.
Mr. Polywhirl
@ Mr.Polywhirl Gracias por aclarar la respuesta. He solucionado el problema de recorte y he publicado la 2.0.9versión. El sitio de demostración se arregla actualizando la versión del paquete. Hay un problema con varios espacios. No sé si es mejor optar por un paquete obstinado o ignorar el problema. Recibí solicitudes para esto de varios lugares. Seguí adelante y agregué recorte de todos modos. Lorem ipsum dolor, sit <many spaces> amet esta fue la razón por la que no lo hice en primer lugar. ¿Qué crees que debería considerar varios espacios y solo eliminar si solo hay uno?
Geon George
Editar: parece que el bloque de código StackOverflow también ignora los espacios múltiples
Geon George
2

No creo que esto sea posible tampoco, pero una solución para esto es crear un <p>elemento y colocarlo con Javascript.

Harmen
fuente
Sí, eso es lo que estoy pensando en hacer. Es solo que con fillText()y strokeText()puedes hacer cosas más allá de lo que CSS puede hacer.
Torre
No he probado esto, pero creo que esta puede ser una mejor solución: las otras soluciones aquí que usan fillText () hacen que el texto no se pueda seleccionar (o presumiblemente pegar).
Jerry Asher
2

Me encontré con esto debido a que tuve el mismo problema. Estoy trabajando con un tamaño de fuente variable, así que esto lo tiene en cuenta:

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

donde .noteContent es el div contenteditable que editó el usuario (esto está anidado en un jQuery para cada función), y ctx.font es "14px Arial" (observe que el tamaño de píxel es lo primero)

MaKR
fuente
0

El elemento Canvas no admite caracteres como la nueva línea '\ n', la pestaña '\ t' o la etiqueta <br />.

Intentalo:

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 

o quizás varias líneas:

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

Mi solución ES5 para el 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
  }
}

Más información sobre el tema está en mi blog .

Oleg Berman
fuente