Scrivere un testo multilinea in un Canvas JavaScript
Quando si ha a che fare con i canvas JavaScript può capitare di dover scrivere un testo.
Lo si può fare con il metodo fillText(text, x, y) o strokeText(text, x, y) del context:
tutto molto semplice... finchè si tratta di una sola linea.
Infatti non c'è nessun metodo che permette di scrivere un testo multilinea, oppure un testo limitato all'interno di un area ben precisa ed andare a capo quando si raggiunge il limite di larghezza.
Per risolvere questo problema ho creato un metodofillTextMultiline(text, x, y, w, h, showBorderLimit):
il codice di esempio produce questo risultato:
Il codice del metodo fillTextMultiline è questo:
il metodo viene aggiunto direttamente al context (CanvasRenderingContext2D).
Lo si può fare con il metodo fillText(text, x, y) o strokeText(text, x, y) del context:
JavaScript
var canvas= document.getElementById("sgart-canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#fff";
ctx.font = "20px Arial";
ctx.fillText('Prova testo', 10, 20);
Infatti non c'è nessun metodo che permette di scrivere un testo multilinea, oppure un testo limitato all'interno di un area ben precisa ed andare a capo quando si raggiunge il limite di larghezza.
Per risolvere questo problema ho creato un metodofillTextMultiline(text, x, y, w, h, showBorderLimit):
JavaScript
var txt = "Esempio testo multilinea.\nScrivo un testo\nmolto lungo con tante parole in modo che vada su più linee - sgart.it";
//ctx.textBaseline = "top";
ctx.textAlign = "left"
ctx.fillStyle = "#fff";
ctx.font = "20px Arial";
ctx.fillTextMultiline(txt, 10, 20, 400, 100, true);
ctx.textAlign = "center"
ctx.font = "30px Arial";
ctx.fillTextMultiline(txt, 110, 130, 400, 100, true);
ctx.textAlign = "right"
ctx.font = "20px Arial";
ctx.fillTextMultiline(txt, 190, 240, 400, 100);
Il codice del metodo fillTextMultiline è questo:
JavaScript
/*
* Sgart.it
* testo multilinea su Canvas
*/
CanvasRenderingContext2D.prototype.fillTextMultiline = function (text, x, y, w, h, showBorderLimit) {
var ctx = this;
// console.log("drawTextMultiline", text, x, y, w, h);
// solo per debug, visualizzo il rettangolo
if (showBorderLimit === true) {
ctx.save();
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#ff0";
ctx.setLineDash([2,4]);
ctx.strokeRect(x, y, w, h);
ctx.restore();
}
// linee di testo risultanti
var resultLines = [];
// creo le linee in base ai ritorni a capo
var lines = text.split(/\n/m);
for (let n = 0; n < lines.length; n++) {
const line = lines[n];
// separo le parole in base agli spazi
var words = line.split(/\s/m);
// conto quante parole stanno in una riga
var s = "";
for (var i = 0, l = 0; i < words.length; i++) {
var word = words[i];
// misuro la lunghezza della parola
var textWidth = ctx.measureText(s + word).width;
// verifico se c'è un ritorno a capo
var isCR = /\n/.test(word);
if (isCR === true) {
resultLines.push(s);
s = "";
l = 0;
} else if (textWidth > w) {
// con questa parola vado oltre la larghezza
resultLines.push(s);
s = word;
l = textWidth;
} else {
// aggiungo la parola alla linea
l = textWidth;
if (s === "") {
s = word;
} else {
s += " " + word;
}
}
}
if (s !== "") {
// aggiungo una riga con le parole rimaste
resultLines.push(s);
}
}
// calcolo l'altezza della riga in base al font corrente
var fontSize = parseInt(ctx.font);
var lineHeight = fontSize * 1.1;
if (h !== undefined) {
// se ho l'altezza impostata creo una area che limita
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.clip();
}
// in base all'allineamento calcolo le coordinate
var offsetX = 0;
if (ctx.textAlign === "center") {
offsetX = w / 2;
} else if (ctx.textAlign === 'right') {
offsetX = w;
}
var offsetY = 0;
if (ctx.textBaseline === "bottom") {
offsetY = lineHeight;
} else if (ctx.textBaseline === 'middle') {
offsetY = lineHeight / 2;
} else if (ctx.textBaseline === 'alphabetic') {
offsetY = fontSize;
}
// stampo le linee
for (var i = 0; i < resultLines.length; i++) {
var line = resultLines[i];
ctx.fillText(line, x + offsetX, y + offsetY + lineHeight * i);
}
};