在JavaScript中获取画布上文本的确切大小。

4

我希望有人能帮助我解决以下问题:我需要获得文本的精确大小。仅测量一个span之类的大小不足以满足我的需求。

Right now, I am using a canvas to find the non-transparent pixels in the canvas. This is my code:

// a function to draw the text on the canvas

let text = "Hello World";
let canvas = document.getElementById('happy-canvas');
let width = 1000
let height = 100
canvas.width = width
canvas.height = height

let ctx = canvas.getContext('2d');
ctx.save();
ctx.font = "30px cursive";
ctx.clearRect(0, 0, width, height);
ctx.fillText(text, 0, 60);

  // get the image data
  let data = ctx.getImageData(0, 0, width, height).data,
    first = false,
    last = false,
    r = height,
    c = 0

  // get the width of the text and convert it to an integer
  const canvWidth = parseInt(ctx.measureText(text).width)

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

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

    canvasHeight = last - first
  }

  //draw a rectangle around the text
  ctx.strokeRect(0, first, canvWidth, canvasHeight)
<div> The last "d" is not completely inside of the the box
  <canvas id="happy-canvas" width="150" height="150"> I wonder what is here</canvas>
</div>

这段代码可以获取文本的准确高度,但无法获取宽度。 目前我使用“measureText”函数,但是该函数的大小会因浏览器和字体不同而有所不同。 如果使用常规字体,则效果相当好。但如果使用更加富有趣味性的字体,则根本无法工作。
下面是一个示例:

https://i.imgur.com/ySOIbDR.png

黑盒子是测量的尺寸。正如你所看到的,“measureText”并没有得到正确的宽度。现在我已经没有任何想法,我还能做什么。

我制作了你的代码片段,你能否用一个可行的示例更新它? - Mark Schultheiss
if (font.capitalize) 中的 font 是什么? - Mark Schultheiss
你可以忽略 if (font.capitalize)。它对于这个例子来说是无关紧要的。上面的代码片段现在是一个可工作的示例。画布的字体是“草书”,“Hello World”的最后一个“d”没有完全在框内。 - pixelbash
你需要在Firefox中检查,因为Chrome似乎不支持字体“cursive”。但在Firefox中它可以正常工作。 - pixelbash
@pixelbash 我知道你说“这个方法可以获取文本的确切高度”,但我不知道为什么你检测非透明行的方法不能用于列。它有什么问题吗? - obscure
@obscure 对于列,它的工作方式不同。但就在2分钟前,我设法让它工作了。我不得不深入挖掘一下getImageData()的工作原理。我将在一分钟内发布我的解决方案。 - pixelbash
1个回答

1

好的,我刚刚让它工作了。

我在做什么? 嗯,在我的情况下,我知道文本始终从 x 值为 0 开始。 因此,文本的长度是由 getImageData() 给出的数组中具有最高 x 值的非透明像素。 所以我正在循环遍历 getImageData() 数组。如果我找到一个 alpha 值大于 0 的像素,我将保存其 x 和 y 值到 highestPixel 中。下次我找到像素时,我将检查其 x 值是否比当前在 highestPixel 中的值更高。如果是,我将使用新值覆盖 highestPixel。最后,我返回 highestPixel,其 x 值将是文本的确切长度。

以下是代码:

// a function to draw the text on the canvas

let text = "Hello World";
let canvas = document.getElementById('happy-canvas');
let width = 1000
let height = 100
canvas.width = width
canvas.height = height

let ctx = canvas.getContext('2d');
ctx.save();
ctx.font = "30px cursive";
ctx.clearRect(0, 0, width, height);
ctx.fillText(text, 0, 60);

// get the image data
let data = ctx.getImageData(0, 0, width, height).data,
  first = false,
  last = false,
  r = height,
  c = 0

// get the width of the text and convert it to an integer
let getPixelwithHighestX = () => {
  let xOfPixel = 0
  let yOfPixel = 0
  let highestPixel = {
    x: 0,
    y: 0
  }
  for (let i = 3; i < data.length; i += 4) {
    if (data[i] !== 0) {
      yOfPixel = Math.floor(i / 4 / width)
      xOfPixel = Math.floor(i / 4) - yOfPixel * width
      if (xOfPixel > highestPixel.x) {
        highestPixel.x = xOfPixel
        highestPixel.y = yOfPixel
      }
    }
  }
  return highestPixel
}

let hightestPixel = getPixelwithHighestX()

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

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

  canvasHeight = last - first
}

//draw a rectangle around the text
ctx.strokeRect(0, first, hightestPixel.x, canvasHeight)
<div> The text is now completely inside the box
  <canvas id="happy-canvas" width="150" height="150"> I wonder what is here</canvas>
</div>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接