HTML 5 Canvas上的垂直对齐文本

时间:2013-03-23 03:14:43

标签: text html5-canvas vertical-alignment

我要做的是在canvas元素的中间显示多行文本。文本是动态的,因为它是由用户使用文本框输入的,然后用用户键入的文本更新画布(下面显示的代码)。我希望文本以与CSS中使用vertical-align:middle属性的方式类似的方式出现在画布上。我的问题是解决这个问题的最简单方法是什么。

我遇到的一个大问题是用户可以更改字体。由于不同的字体是不同的高度(即使它们被定义在px高度,它们也不是那么高的高度)。到目前为止,我最好的想法是计算画布上文本的高度。我在网站How can you find the height of text on an HTML canvas?上阅读了这篇文章,请参阅Daniel的第二个回答。这应该计算文本的实际高度,然后可以使用该高度来计算文本的正确起始位置以使其在画布上居中。基于我下面的代码,我相信我必须基本上运行类似的代码来预先确定字体的正确起始位置。

这是我在画布上正确包装和显示文本的方法:

    function wrapText(context, text, x, y, maxWidth, lineHeight) {
    //manage carriage return
    text = text.replace(/(\r\n|\n\r|\r|\n)/g, "\n");
    //manage tabulation
    text = text.replace(/(\t)/g, "    "); // I use 4 spaces for tabulation, but you can use anything you want
    //array of lines
    var sections = text.split("\n"); 

     for (s = 0, len = sections.length; s < len; s++) {
          var words = sections[s].split(' ');
          var line = '';

          for (var n = 0; n < words.length; n++) {
              var testLine = line + words[n] + ' ';
              var metrics = context.measureText(testLine);
              var testWidth = metrics.width;
              if (testWidth > maxWidth) {
                  context.fillText(line, x, y);
                  line = words[n] + ' ';
                  y += lineHeight;
              } else {
                  line = testLine;
              }
          }
          context.fillText(line, x, y);

         //new line for new section of the text
         y += lineHeight;
      }
}

      var canvas = document.getElementById('myCanvas');
      var context = canvas.getContext('2d');
      var maxWidth = 350;
      var lineHeight = 25;
      var x = (canvas.width - maxWidth) / 2;
      var y = 60;
      var text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. \nLorem Ipsum has been the industry's standard dummy text ever since the 1500s.";

      context.font = '14pt Verdana';
      context.fillStyle = '#000';

      wrapText(context, text, x, y, maxWidth, lineHeight); 

我想知道是否还有其他方法,我没有考虑过这会简化问题,或者这种方法是否是最好的方法?在canvas元素上是一种更简单的方法来垂直对齐类似于CSS的文本吗?

3 个答案:

答案 0 :(得分:3)

酷!

我看了你的参考资料:How can you find the height of text on an HTML canvas?

我为 Prestaul的答案设置了时间测试,该答案实际上使用画布来像素检查文本的下限和上限。在我的测试中,我使用所有大写和小写字母(a-z和A-Z)而不是他的小/大“gM”。我还使用了在JS中创建的画布,我没有添加到DOM中。

结果:我能够每秒反复运行900 +/-次。

我没有运行Daniel测试操作DOM元素进行测量,但我假设比Prestaul的1+毫秒(!)结果慢。

我的结论是,只要用户更改字体,我就会使用Prestaul的方法测试最大高度。

就个人而言,攻击DOM可能会有效,但对我而言,感觉就像使用黑盒子,有一天可能会在浏览器更新后包含炸弹。

感谢我的好奇心 - 我很享受!

[编辑包含压力测试代码]

以下是压力测试和小提琴的代码:http://jsfiddle.net/m1erickson/ttsjq/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; padding:50px; }
</style>

<script>
$(function(){

    var canvas=document.createElement("canvas");
    canvas.width=1000;
    canvas.height=50;
    var ctx=canvas.getContext("2d");

    function measureTextHeight(left, top, width, height,text) {

        // Draw the text in the specified area
        ctx.save();
        ctx.clearRect(0,0,canvas.width,canvas.height);
        ctx.fillText(text, 0, 35); // This seems like tall text...  Doesn't it?
        ctx.restore();

        // Get the pixel data from the canvas
        var data = ctx.getImageData(left, top, width, height).data,
            first = false, 
            last = false,
            r = height,
            c = 0;

        // Find the last line with a non-white pixel
        while(!last && r) {
            r--;
            for(c = 0; c < width; c++) {
                if(data[r * width * 4 + c * 4 + 3]) {
                    last = r;
                    break;
                }
            }
        }

        // Find the first line with a non-white pixel
        while(r) {
            r--;
            for(c = 0; c < width; c++) {
                if(data[r * width * 4 + c * 4 + 3]) {
                    first = r;
                    break;
                }
            }

            // If we've got it then return the height
            if(first != r) return last - first;
        }

        // We screwed something up...  What do you expect from free code?
        return 0;
    }

    ctx.font='32px Arial';
    var start = new Date().getTime();
    var text="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    for(var x=0;x<1000;x++){
      var height = measureTextHeight(0, 0, canvas.width, canvas.height,text);
    }
    console.log(measureTextHeight(0, 0, canvas.width, canvas.height,text));
    console.log((new Date().getTime()-start));



}); // end $(function(){});
</script>

</head>

<body>
    <div>Stress testing Prestaul's measureTextHeight function...</div>
</body>
</html>

答案 1 :(得分:3)

要在画布中设置垂直对齐文本,请使用textBaseline。例如:

ctx.beginPath();
ctx.font = "10px arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle"; // set text in center of place vertically
ctx.fillText("sample text", 100, 100);
ctx.closePath();

答案 2 :(得分:1)

ctx.textAlign = "center";
ctx.textBaseline = "middle"; 
const fix = ctx.measureText("H").actualBoundingBoxDescent / 2; // Notice Here
ctx.fillText("H", convasWidth / 2, canvaseHeight / 2 + fix);

请参见codepen demo