HTML5 canvas ctx.fillText non farà interruzioni di riga?


108

Non riesco ad aggiungere testo a una tela se il testo include "\ n". Voglio dire, le interruzioni di riga non mostrano / funzionano.

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

Il codice sopra verrà disegnato "s ome \n <br/> thing", su una riga.

È una limitazione di fillText o sto sbagliando? le "\ n" sono lì e non vengono stampate, ma non funzionano neanche.


1
vuoi andare a capo automaticamente quando raggiungi la fine? o semplicemente per prendere in considerazione i caratteri di nuova riga presenti nel testo?
Gabriele Petrioli

Avvolgi il testo in più righe.
Torre

Ciao twodordan, questa limitazione esiste sia su Chrome che su Mozilla? Le persone spesso usano un semplice testo html che mettono sulla tela con una posizione: assoluta per esempio. Inoltre puoi fare due fillText e spostare l'origine Y del tuo testo per le tue seconde righe.
Tim


TL; DR: chiama fillText()più volte e usa l'altezza del carattere per separare, oppure usa developer.mozilla.org/en-US/docs/Web/API/TextMetrics developer.mozilla.org/en-US/docs/Web/API /… - oppure usa una delle "soluzioni" molto complicate di seguito che non utilizzano TextMetrics ...
Andrew

Risposte:


62

Temo che sia una limitazione di Canvas ' fillText. Non c'è supporto multi-linea. Quel che è peggio, non esiste un modo integrato per misurare l'altezza della linea, ma solo la larghezza, rendendo ancora più difficile farlo da soli!

Molte persone hanno scritto il proprio supporto multilinea, forse il progetto più notevole che ha è Mozilla Skywriter .

L'essenza di ciò che dovrai fare è più fillTextchiamate aggiungendo ogni volta l'altezza del testo al valore y. (misurare la larghezza di M è ciò che fanno gli skywriter per approssimare il testo, credo.)


Grazie! Avevo la sensazione che sarebbe stato fastidioso ... Bello sapere di SKYWRITER, ma "aspetterò" fino a quando fillText () non sarà migliorato. Non era un affare terribilmente importante nel mio caso. Hah, nessuna altezza di linea, è come se qualcuno l'avesse fatto apposta. : D
Spectraljump

18
Onestamente, non tratterrei il respiro se fillText () viene "migliorato" per supportare questo, poiché ho la sensazione che questo sia il modo in cui dovrebbe essere utilizzato (chiamate multiple e calcolo di yOffset da soli). Penso che gran parte della potenza dell'API canvas sia che separa la funzionalità di disegno di livello inferiore da ciò che puoi già fare (esegui le misurazioni necessarie). Inoltre, puoi conoscere l'altezza del testo semplicemente fornendo la dimensione del testo in pixel; in altre parole: context.font = "16px Arial"; - hai l'altezza lì; la larghezza è l'unica dinamica.
Lev

1
Alcune proprietà aggiuntive per measureText()sono state aggiunte che credo potrebbe risolvere il problema. Chrome ha un flag per abilitarli, ma altri browser no ... ancora!
SWdV

@SWdV solo per essere chiari, quelli sono stati nelle specifiche da anni ormai, potrebbero volerci anni prima che avremo un'adozione abbastanza ampia da usare :(
Simon Sarris

67

Se vuoi solo prenderti cura dei caratteri di nuova riga nel testo puoi simularlo dividendo il testo in corrispondenza di nuove righe e chiamando più volte il fillText()

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


Ho appena creato un esempio di prova del concetto ( avvolgimento assoluto alla larghezza specificata. Nessuna parola di manipolazione che si interrompe, ancora )
su 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 una prova di concetto che avvolge le parole ( interrompendo gli spazi ).
esempio su 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>


Nel secondo e nel terzo esempio sto usando il measureText()metodo che mostra quanto sarà lunga ( in pixel ) una stringa quando verrà stampata.


come giustificare l'intero testo lungo?
Amirhossein Tarmast

Se hai bisogno di un testo lungo e giustificato, perché dovresti usare una tela?
Mike 'Pomax' Kamermans il

39

Forse arrivo a questa festa un po 'tardi, ma ho trovato perfetto il seguente tutorial per avvolgere il testo su una tela.

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

Da quello sono stato in grado di pensare che funzionassero più linee (scusa Ramirez, la tua non ha funzionato per me!). Il mio codice completo per avvolgere il testo in una tela è il seguente:

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

Dov'è cl'ID della mia tela ed textè l'ID della mia casella di testo.

Come puoi probabilmente vedere sto usando un carattere non standard. Puoi usare @ font-face fintanto che hai usato il carattere su del testo PRIMA di manipolare la tela, altrimenti la tela non raccoglierà il carattere.

Spero che questo aiuti qualcuno.


26

Dividi il testo in righe e disegnale separatamente:

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

Ecco la mia soluzione, modificando la popolare funzione wrapText () che è già presentata qui. Sto usando la funzionalità di prototipazione di JavaScript in modo da poter chiamare la funzione dal contesto della 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;
    }
}

Utilizzo di base:

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

Ecco una dimostrazione che ho messo insieme: http://jsfiddle.net/7RdbL/


Ha funzionato come un fascino. Grazie.
couzzi

13

Ho appena esteso CanvasRenderingContext2D aggiungendo due funzioni: mlFillText e mlStrokeText.

Puoi trovare l'ultima versione in GitHub :

Con questa funzione puoi riempire / accarezzare il testo miltiline in una casella. Puoi allineare il testo verticalmente e orizzontalmente. (Tiene conto di \ ne può anche giustificare il testo).

I prototipi sono:

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

Dove vAlign può essere: "top", "center" o "button" E hAlign può essere: "left", "center", "right" o "justify"

Puoi testare la libreria qui: http://jsfiddle.net/4WRZj/1/

inserisci qui la descrizione dell'immagine

Ecco il codice della libreria:

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

Ed ecco l'esempio d'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);

Uncaught ReferenceError: Words is not definedSe provo a cambiare tipo di carattere. Ad esempio: ctx.font = '40px Arial';- prova a metterlo nel tuo violino
psycho brm

A proposito, da dove diavolo viene la Wordsvariabile (case-sensitive) ?? Non è definito da nessuna parte. Quella parte del codice viene eseguita solo quando cambi il carattere ..
psycho brm

1
@psychobrm Hai assolutamente ragione. È un bug (l'ho già risolto). Questa parte di codice viene eseguita solo se devi dividere una parola in due righe. Grazie!
jbaylina

Ho apportato alcuni aggiornamenti di cui avevo bisogno: rendering di spazi, rendering di newline iniziali / finali, rendering di tratti e riempimento con una chiamata (non misurare il testo due volte), ho anche dovuto modificare l'iterazione, poiché for innon funziona bene con Extended Array.prototype. Potresti metterlo su GitHub in modo che possiamo iterarlo?
psycho brm

@psychobrm Ho unito le tue modifiche. Grazie!
jbaylina

8

Utilizzando javascript ho sviluppato una soluzione. Non è bello ma ha funzionato per me:


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

Spero che aiuti!


1
ciao, supponiamo che il mio testo è come questo var text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; allora cosa è successo in tela ???
Amol Navsupe

Uscirà dalla tela, poiché @Ramirez non ha inserito il parametro maxWidth in fillText :)
KaHa6uc

6

Il codice per il wrapping delle parole ( interruzione negli spazi) fornito da @Gaby Petrioli è molto utile. Ho esteso il suo codice per fornire supporto per i caratteri di nuova riga \n. Inoltre, spesso è utile avere le dimensioni del riquadro di delimitazione, quindi multiMeasureText()restituisce sia la larghezza che l'altezza.

Puoi vedere il codice qui: http://jsfiddle.net/jeffchan/WHgaY/76/


i link scadono, inserisci il codice in questa risposta anche se hai un link funzionante. Se jsfiddle si spegne, questa risposta diventa completamente inutile così com'è.
Mike 'Pomax' Kamermans il

5

Ecco una versione di Colin wrapText()che supporta anche il testo centrato 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;
    }
};

5

Se sono necessarie solo due righe di testo, è possibile suddividerle in due chiamate fillText diverse e assegnare a ciascuna una linea di base diversa.

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

4

Questa domanda non sta pensando in termini di come funziona la tela. Se vuoi un'interruzione di riga, regola semplicemente le coordinate del tuo prossimo ctx.fillText.

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

3

Penso che tu possa ancora fare affidamento sui CSS

ctx.measureText().height doesnt exist.

Fortunatamente, tramite hack-ardry CSS (vedere Metriche tipografiche per ulteriori modi per correggere le implementazioni precedenti dell'uso delle misurazioni CSS), possiamo trovare l'altezza del testo misurando l'offsetHeight di a con le stesse proprietà dei caratteri:

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

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


Questa è una buona soluzione se hai la memoria per costruire un elemento del genere ogni volta che devi misurare. Puoi anche ctx.save()allora, ctx.font = '12pt Arial' quindi parseInt( ctx.font, 10 ),. Nota che uso 'pt' quando lo imposto. Quindi si tradurrà in PX e potrà trasformarsi in una cifra per il consumo come l'altezza del carattere.
Eric Hodonsky

3

Ho creato una piccola libreria per questo scenario qui: Canvas-Txt

Rende il testo su più righe e offre modalità di allineamento decenti.

Per utilizzarlo, sarà necessario installarlo o utilizzare un CDN.

Installazione

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)

Questo renderà il testo in una casella invisibile con posizione / dimensioni di:

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

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


Sono andato avanti e ho definito alcune variabili per aiutare a "auto-documentare" l'esempio. Gestisce anche la centratura del riquadro di delimitazione all'interno della tela. Ho anche aggiunto un rettangolo dietro, quindi puoi effettivamente vederlo centrato in relazione. Ottimo lavoro! +1 Una piccola cosa che ho notato è che le linee che vanno a capo non avranno i loro spazi iniziali soppressi. Potresti voler tagliare ogni riga, ad esempio l' ctx.fillText(txtline.trim(), textanchor, txtY)ho notato solo nella tua demo interattiva sul tuo sito web.
Mr. Polywhirl

@ Mr.Polywhirl Grazie per aver chiarito la risposta. Ho risolto il problema del trim e pubblicato la 2.0.9versione. Il sito demo viene risolto aggiornando la versione del pacchetto. Si è verificato un problema con più spazi. Non so se sia meglio andare con un pacchetto supponente o ignorare il problema. Ho ricevuto richieste per questo da più posti. Sono andato avanti e ho aggiunto il trim comunque. Lorem ipsum dolor, sit <many spaces> amet questo era il motivo per cui non l'ho fatto in primo luogo. Cosa pensi che dovrei considerare più spazi e rimuovere solo se ce n'è uno solo?
Geon George,

Modifica: sembra che il blocco di codice StackOverflow ignori anche più spazi
Geon George

2

Non penso che questo sia possibile, ma una soluzione alternativa è creare un <p>elemento e posizionarlo con Javascript.


Sì, è quello che sto pensando di fare. È solo che con fillText()e strokeText()puoi fare cose oltre a ciò che i CSS possono fare.
Torre

Non l'ho testato, ma penso che questa possa essere una soluzione migliore: le altre soluzioni qui usando fillText () fanno in modo che il testo non possa essere selezionato (o presumibilmente incollato).
Jerry Asher

2

Mi sono imbattuto in questo a causa dello stesso problema. Sto lavorando con una dimensione del carattere variabile, quindi questo ne tiene conto:

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

dove .noteContent è il div contenteditable modificato dall'utente (questo è annidato in una jQuery ogni funzione), e ctx.font è "14px Arial" (nota che la dimensione dei pixel viene prima)


0

L'elemento Canvas non supporta caratteri come la nuova riga "\ n", la scheda "\ t" o il tag <br />.

Provalo:

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 forse più righe:

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

La mia soluzione ES5 per il 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
  }
}

Maggiori informazioni sulla questione sono sul mio blog .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.