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:
var canvas= document.getElementById("sgart-canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#fff";
ctx.font = "20px Arial";
ctx.fillText('Prova testo', 10, 20);
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 metodo
fillTextMultiline(text, x, y, w, h, showBorderLimit):
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 di esempio produce questo risultato:
Il codice del metodo
fillTextMultiline è questo:
/*
* 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);
}
};
il metodo viene aggiunto direttamente al context (CanvasRenderingContext2D).