【问题标题】:Determine width of string in HTML5 canvas确定 HTML5 画布中字符串的宽度
【发布时间】:2012-04-23 08:41:00
【问题描述】:

我正在 HTML5 画布上的图像(模因生成器)上绘制文本。我想计算字体大小,以便字符串跨越画布的整个宽度。

所以基本上,JavaScript 中有没有一种方法可以确定对于给定宽度的某种字体的某些字符串我应该使用什么字体大小?

【问题讨论】:

    标签: javascript html text canvas fonts


    【解决方案1】:

    fillText() 方法有一个可选的第四个参数,即渲染字符串的最大宽度

    MDN 的文档说...

    最大宽度

    可选;要绘制的最大宽度。如果指定,则字符串为 计算得比这个宽度更宽,字体被调整为使用 更水平压缩的字体(如果有可用的或者如果 可以通过缩放当前字体来合成合理可读的字体 水平)或更小的字体。

    但是,在撰写本文时,跨浏览器还不能很好地支持此参数,这导致第二个解决方案使用measureText() 来确定字符串的尺寸而不渲染它。

    var width = ctx.measureText(text).width;
    

    我可以这样做...

    var canvas = document.getElementById('canvas'),
        ctx = canvas.getContext('2d');
    
    // Font sizes must be in descending order. You may need to use `sort()`
    // if you can't guarantee this, e.g. user input.
    var fontSizes = [72, 36, 28, 14, 12, 10, 5, 2],
        text = 'Measure me!';
    
    // Default styles.
    ctx.textBaseline = 'top';
    ctx.fillStyle = 'blue';
    
    var textDimensions,
        i = 0;
    
    do {
       ctx.font = fontSizes[i++] + 'px Arial';
       textDimensions = ctx.measureText(text);        
    } while (textDimensions.width >= canvas.width);
    
    ctx.fillText(text, (canvas.width - textDimensions.width) / 2, 10);​
    

    jsFiddle.

    我有一个按降序排列的字体大小列表,我遍历该列表,确定渲染的文本是否适合画布尺寸。

    如果可以,我将文本中心对齐。如果您必须在文本的左侧和右侧进行填充(这样看起来会更好),请在计算文本是否适合时将填充值添加到 textDimensions.width

    如果您要尝试的字体大小列表很长,最好使用binary search algorithm。但是,这会增加代码的复杂性。

    例如,如果您有 200 种字体大小,则通过数组元素的线性 O(n) 迭代可能会非常慢。

    二进制截断应该是 O(log n)。

    这是函数的胆量

    var textWidth = (function me(fontSizes, min, max) {
    
        var index = Math.floor((min + max) / 2);
    
        ctx.font = fontSizes[index] + 'px Arial';
    
        var textWidth = ctx.measureText(text).width;
    
        if (min > max) {
            return textWidth;
        }
    
        if (textWidth > canvas.width) {
            return me(fontSizes, min, index - 1);
        } else {
            return me(fontSizes, index + 1, max);
        }
    
    })(fontSizes, 0, fontSizes.length - 1);
    

    jsFiddle.

    【讨论】:

    • 我早该知道你不会因为不遵守而获得 90k+ 代表。 :) 非常好。
    • 我正在尝试在通过文本字段的onkeyup 属性运行的函数中使用它,并且每当我输入该字段时,我的浏览器都会冻结并且必须强制退出。有什么想法吗?
    • @edc1591 你能设置一个小提琴来演示吗? do/while 循环的表达式可能永远不会评估为 true。
    • 看来我只是将代码粘贴到我的 js 文件中的错误位置。它现在正在工作。二进制搜索算法也很好用,非常感谢!
    • 我认为这看起来像 do while 可能会永远循环。我不确定当字体为"undefinedpx Arial" 时浏览器会做什么
    【解决方案2】:
    1. 为文本写下你想要的所有东西。 (例如ctx.font

    2. 在编写实际文本之前使用ctx.measureText("myText").width获取文本的宽度,因为我们在ctx上进行了更改后调用了它,所以每次更改ctx属性(例如字体)都会不同

    3. 现在我们将从中间缩放它。

       ctx.translate(midPoint_X_Of_Text, midPoint_Y_Of_Text);
       ctx.scale(desiredWidth/measuredWidth, desiredWidth/measuredWidth);
       ctx.translate(-midPoint_X_Of_Text, -midPoint_Y_Of_Text);
      
    4. 写文字ctx.fillText()ctx.strokeText()

    5. 通过ctx.save()ctx.restore() 或手动取消所有更改,如setTransform()

    var can = document.getElementById('myCan');
    var ctx = can.getContext("2d");
    
    var desiredWidth = can.width;
    var myText = "Hello How Are You?"
    
    function draw(){
      ctx.font = can.height/2 + "px verdana";
      ctx.fillStyle = "red";
      ctx.textAlign = "center";
      ctx.textBaseline = "middle";
      let measuredWidth = ctx.measureText(myText).width;
      let ratio = desiredWidth/measuredWidth;
      ctx.translate(can.width/2,can.height/2);
      ctx.scale(ratio,ratio);
      ctx.translate(-can.width/2,-can.height/2);
      ctx.fillText(myText,can.width/2,can.height/2);
      ctx.setTransform();
    }
    
    draw();
    
    let myInput = document.getElementById('myInput');
    myInput.addEventListener('input',(e)=>{
        ctx.clearRect(0,0,can.width,can.height);
            myText = myInput.value;
            draw();
    })
    <html>
    <body>
      <center>
        <input id="myInput" value="Hello How Are You?"/><br>
        <canvas id="myCan" height="300" width=300 style="border:black solid 2px;">
        </canvas>
      </center>
    </body>
    </html>

    【讨论】:

      【解决方案3】:

      我有下一个代码,它非常适合我。也许有一点错误,但我不需要列出字体大小。我只是用字体大小获得宽度,如果它太大,我会根据画布宽度和填充(50)计算比例大小

      ctx.font = FONT_SIZE+"pt Verdana";
      var textWidth = ctx.measureText(markText).width;
      if(textWidth > canvas.width - 50) {
          ctx.font = parseInt(FONT_SIZE*(canvas.width-50)/textWidth)+"pt Verdana";
          textWidth = ctx.measureText(markText).width;
      }
      ctx.textBaseline = "middle";
      ctx.fillStyle = "rgba(255,255,255,0.5)";
      ctx.strokeStyle = "rgba(0,0,0,0.5)";
      var xT = image.width / 2 - textWidth / 2;
      var yT = image.height / 2;
      ctx.fillText(markText, xT, yT);
      ctx.strokeText(markText, xT, yT);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-04
        • 1970-01-01
        • 2012-10-15
        • 1970-01-01
        • 2015-01-25
        • 1970-01-01
        • 1970-01-01
        • 2011-05-01
        相关资源
        最近更新 更多