1

我想做的是在画布元素的中间显示多行文本。文本是动态的,因为它是由用户使用文本框输入的,然后画布会更新为用户输入的文本(代码如下所示)。我希望文本出现在画布上的方式与使用 CSS 中的垂直对齐:中间属性显示文本的方式类似。我的问题是解决这个问题的最简单方法是什么。

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

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

    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); 

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

4

3 回答 3

7

用于在画布中设置垂直对齐文本使用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();
于 2018-10-24T11:46:57.420 回答
3

凉爽的!

我查看了您的参考资料:如何在 HTML 画布上找到文本的高度?

我为Prestaul 的答案设置了一个时间测试,它实际上使用画布来像素检查文本的下限和上限。在我的测试中,我使用了所有大写和小写字母(az 和 AZ),而不是他的小/大“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>
于 2013-03-23T05:14:32.897 回答
2
ctx.textAlign = "center";
ctx.textBaseline = "middle"; 
const fix = ctx.measureText("H").actualBoundingBoxDescent / 2; // Notice Here
ctx.fillText("H", convasWidth / 2, canvaseHeight / 2 + fix);

codepen 演示

于 2020-08-04T03:36:24.427 回答