Canvas Wrap + Newlines

A nice canvas wrap text functionality plus the ability to manually set newlines

by Kikketer

HTML

<canvas id="cvs"></canvas>
<br />
<input type="text" id="input" value="Here's a\nnew line! and some other text that will do a natural line wrap." />

CSS

body {
  background-color: #efefef;
}

canvas {
  outline: 1px solid #000;
  background-color: white;
}

JavaScript

var canvas = document.getElementById('cvs'),
  ctx = canvas.getContext('2d'),
  input = document.getElementById('input'),
  width = +(canvas.width = 400),
  height = +(canvas.height = 250),
  fontFamily = "Arial",
  fontSize = "24px",
  fontColour = "blue";

function fragmentText(text, maxWidth) {
  var words = text.split(' ');
  var lines = [];
  var line = '';

  while (words.length > 0) {
    while (ctx.measureText(words[0]).width >= maxWidth) {
      forcedNewline = false;
      var tmp = words[0];
      words[0] = tmp.slice(0, -1);
      if (words.length > 1) {
        words[1] = tmp.slice(-1) + words[1];
      } else {
        words.push(tmp.slice(-1));
      }
    }

    if (words[0].match(/\\n/g)) {
      var twoWords = words[0].split('\\n');
      // Take the first half and add it to the line
      line += twoWords[0];
      // Loop to create any more newlines (\n\n anyone?)
      for (var i = 1; i <= twoWords.length - 2; i++) {
        lines.push(line);
        line = '';
      }
      // Take the second half and replace words[0] with it
      words.splice(0, 1, twoWords[twoWords.length - 1]);
      lines.push(line);
      line = '';
    } else if (ctx.measureText(line + words[0]).width < maxWidth) {
      line += words.shift() + " ";
    } else {
      lines.push(line);
      line = "";
    }

    if (words.length === 0) {
      lines.push(line);
    }
  }
  return lines;
}

function draw() {
  ctx.save();
  ctx.clearRect(0, 0, width, height);
  ctx.font = "bold " + fontSize + " " + fontFamily;
  ctx.textAlign = "left";
  ctx.fillStyle = fontColour;
  var lines = fragmentText(input.value, width - parseInt(fontSize, 0));
  lines.forEach(function(line, i) {
    ctx.fillText(line, 0, (i + 1) * parseInt(fontSize, 0));
  });
  ctx.restore();
}

input.onkeyup = function(e) {
  draw();
};

// Initial draw
draw();