Formato de texto en canvas HTML

Estoy haciendo un juego usando HTML Canvas y vanilla javascript. Soy nuevo en JavaScript, así que esto puede ser más fácil de lo que creo. Tengo dos matrices de objetos que representan tarjetas, cada objeto tiene una propiedad de “texto” de entre 40 y 100 caracteres que se dibujan dinámicamente en la pantalla en una tarjeta que tiene 130 x 70 px.

Necesito formatear el texto para que se ajuste a la restricción de ancho de la tarjeta (130px) y crear una nueva línea cuando sea necesario.

Cualquier ayuda es apreciada

Editado para aclarar

Puede utilizar el método measureText() de la API de canvas.
Como lo señaló Ken Fyrstenberg en esta asombrosa respuesta ,

canvas ‘ measureText no admite actualmente la altura de medición (ascenso + descenso).

El bash siguiente utiliza un valor de lineHeight codificado que tendría que encontrar antes de representar texto. La respuesta de Ken proporciona una manera de encontrarlo programáticamente.

[ Editar : gracias al comentario de markE a continuación, ahora utiliza una aproximación de 1.286 * el tamaño de la fuente. ]

Así que aquí está, sucio y debe haber mejores formas de hacerlo, pero de todos modos …

 var input = document.querySelector('input'); input.addEventListener('keyup', write, false); var c = document.createElement('canvas'),ctx = c.getContext('2d'); c.width = 400, c.height = 150; document.body.appendChild(c); // simple box object for the card var card = {x: 25, y: 25, w: 130, h: 70}; ctx.fillStyle = "#CCCCCC"; ctx.fillRect(card.x, card.y, card.w, card.h); var fontSize = 12; ctx.font=fontSize+'px arial'; // Margins multipliers are chosen arbitrarly here, just because they fit well to my eyes var margins = {t: 1.25*fontSize, l: .7*fontSize, b: 2*fontSize, r: .7*fontSize}, marginsX = margins.l+margins.r, marginsY = margins.t+margins.b; // As suggested by markE, lineHeight is set to 1.286* the fontSize, // for a calculated way, see Ken's answer here : https://stackoverflow.com/a/17631567/3702797 var lineHeight = 1.286*fontSize; // just a shortcut var lineWidth = function(text) { return ctx.measureText(text).width; }; function write() { var txt = this.value; // Redraw our card ctx.fillStyle = "#CCCCCC"; ctx.fillRect(card.x, card.y, card.w, card.h); // Split the input at any white space, you might want to change it var txtLines = txt.split(/\s/); var i = 0, maxWidth = card.w - marginsX; if(lineWidth(txt[0])>card.w || lineHeight>card.h-(margins.t/4) ){ console.warn('TOO BIG FONT!!'); return; } while(txtLines[i]) { // If our current line merged with the next line width isn't greater than the card one if (txtLines[i+1] && lineWidth(txtLines[i] + ' ' + txtLines[i+1]) < maxWidth) { // Merge them txtLines[i] += ' ' + txtLines.splice(i + 1, 1); } else { // Is the one word too big? --> Dirtyphenation ! if (lineWidth(txtLines[i]) > maxWidth) { // Add a new index with the two last chars since we'll add a dash txtLines.splice(i+1, 0, ""); // If it's still too big while (lineWidth(txtLines[i]) > maxWidth) { var lastChars = txtLines[i].length - 2; // Append those two last chars to our new array index txtLines[i+1] = txtLines[i].substring(lastChars) + txtLines[i+1]; // Remove them from our current index txtLines[i] = txtLines[i].substring(0, lastChars); } // Add the final dash txtLines[i] += "-"; } // Is our text taller than the card height? if (lineHeight*i > card.h-marginsY){ // If there is more text coming after... if (txtLines[i+1]){ // ...and it fits in the line if(lineWidth(txtLines[i]+' '+txtLines[i+1]) maxWidth){ txtLines[i] = txtLines[i].substring(0,txtLines[i].length-1) } }else{ return; } txtLines[i] += '...'; // remove the surplus from the array txtLines = txtLines.slice(0,i+1); } } // stop looping here since we don't have space anymore break; } // Go to next line i++; } } ctx.fillStyle = "#000"; // Where to draw var x = card.x + (margins.l); var y = card.y + (margins.t); // Iterate through our lines for (var i = 0; i < txtLines.length; i++) { ctx.fillText(txtLines[i], x, y + (i * lineHeight)); } } 
 canvas {border: 1px solid}