【问题标题】:Formatting text on HTML canvas [closed]在 HTML 画布上格式化文本 [关闭]
【发布时间】:2015-04-10 16:13:34
【问题描述】:

我正在使用 HTML Canvas 和 vanilla javascript 制作游戏。我是 javascript 新手,所以这可能比我想象的要容易。我有两个代表卡片的对象数组,每个对象都有一个介于 40-100 个字符之间的“文本”属性,该属性在屏幕上动态绘制到 130 x 70 像素的卡片上。

我需要格式化文本以适应卡片的宽度限制 (130px) 并在必要时创建一个新行。

感谢任何帮助

已编辑以更清晰

【问题讨论】:

标签: javascript html canvas


【解决方案1】:

您可以使用 canvas API 中的 measureText() 方法。
正如Ken Fyrstenbergthis awesome answer 中所指出的,

canvas'measureText 目前不支持测量高度(上升 + 下降)。

下面的尝试使用一个硬编码的lineHeight 值,您必须在呈现文本之前找到它。 Ken's answer 确实提供了一种以编程方式查找它的方法。

[ 编辑:感谢 markE 在下面的评论,它现在使用 1.286*字体大小的近似值。 ]

就是这样,很脏,必须有更好的方法来做到这一点,但无论如何......

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){
               continue;
            }
            // ...and it doesn't fit in the line
            else{
	            // Does a single char fit with the ellipsis ?
                if(lineWidth(txtLines[i][0]+'...')<maxWidth){
                    // remove a char until we can put our ellipsis
					while (lineWidth(txtLines[i]+'...') > 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}
&lt;input type="text" /&gt;

【讨论】:

  • 行高的粗略估计是 1.286 * font-size。
  • 它又快又粗,但除非你的字体是“古怪的”,带有令人发指的上升或下降,否则你应该没问题。估计在超小和超大字体大小时确实会变得不稳定,但这似乎不适用于提问者 130x70 卡片大小。 ;-)
  • 非常感谢,正是我需要的,辛苦了!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多