15

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

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

4

3 回答 3

32

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

js小提琴

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

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

如果您有一长串要尝试的字体大小,最好使用二分搜索算法。但是,这会增加代码的复杂性。

例如,如果您有 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);

js小提琴

于 2012-04-11T03:01:20.933 回答
1
  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>

于 2021-05-14T15:20:12.937 回答
0

我有下一个代码,它非常适合我。也许有一点错误,但我不需要列出字体大小。我只是用字体大小获得宽度,如果它太大,我会根据画布宽度和填充(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);
于 2014-01-27T19:13:59.203 回答