Passaggio all'ora solare 27 ottobre 2019 03:0002:00 sposta indietro l'orologio di 1 ora (si dorme 1 ora in più)
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 metodofillTextMultiline(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).