确定 HTML5 画布中字符串的宽度

Determine width of string in HTML5 canvas

本文关键字:字符串 布中 HTML5 确定      更新时间:2023-09-26

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

所以基本上,JavaScript 中有没有办法确定我应该为给定宽度的某种字体的某个字符串使用什么字体大小?

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

如果您有很长的字体大小列表要尝试,则最好使用二进制搜索算法。但是,这将增加代码的复杂性。

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

  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>

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

使用第二个临时 canvas2D

 function determineWidth(text, font) {
     var textCtx = document.createElement("canvas").getContext("2d");
     textCtx.font = font
     let measure = textCtx.measureText(text);
     let fontHei = parseInt(font.slice(0, font.indexOf("px")))
     return [measure.width,fontHei]
 }

在创建真实画布之前使用它

   let font = `40px monospace`;
   let [wid,hei]=determineWidth(text,font)
   let textAlign = "center";
   let textBaseline = "middle";
   let fillStyle = "black"; 
   var textCtx = document.createElement("canvas").getContext("2d");
            
   textCtx.canvas.width = wid;
   textCtx.canvas.height = hei;
          
   textCtx.font = font
   textCtx.textAlign = textAlign
   textCtx.textBaseline = textBaseline
   textCtx.fillStyle = fillStyle
   textCtx.clearRect(0, 0, textCtx.canvas.width, textCtx.canvas.height);
   textCtx.fillText(text, textCtx.canvas.width / 2, textCtx.canvas.height / 2);