Fabric JS:文本对象的选择区域大于文本的实际大小

Sag*_*ani 5 fabricjs

我们正在使用Fabric JS将文本和图像对象添加到画布.

但是当我们添加任何文本对象并选择它时,它会显示大于文本实际大小的选择区域.

请参阅此链接以查看屏幕截图.

我们使用以下代码添加文本对象.

var text_object = new fabric.Text('00', {
                    fontSize: 192,
                    fontFamily: 'Times New Roman',
                    padding: 0
                  });
text_object.top = parseInt((canvas.height - text_object.height) / 2);
text_object.left = parseInt((canvas.width - text_object.width) / 2);
text_object.lockUniScaling = true;
canvas.add(text_object);
canvas.renderAll();
canvas.setActiveObject(text_object);

主要问题是当我们尝试获取添加的文本对象的宽度和高度时; 它给出了文本对象选择区域的宽度和高度,而不是文本的实际大小.

我们如何使文本对象的选择区域与文本的实际大小相同?

或者如何在没有选择区域的情况下获得文本的实际大小?

小智 3

没有直接的方法可以实现你想要的,那就是文本矢量路径的边界框。

如果您同意近似解决方案,请将文本渲染到空画布,使用 getImageData 检查所有像素的透明度,并找出找到填充点的最大和最小 x 和 y。它远非完美,但您不会轻易获得更好的解决方案。

我想服务器端可能有解决方案,但我不知道。

理论上,您可以在 JS 中创建自己的字体渲染引擎,因此您将拥有完美的边界框,但是,除了简单/玩具案例之外,一个严肃的文本布局引擎需要数年才能实现,所以我想这完全超出了范围。

如果您需要近似方法的帮助,请告诉我,我会提供一个例子。

编辑提供了一个例子:

function getVisiblePixelsBoundingBox(canvas) {
    const context = canvas.getContext("2d");
    const imageData = context.getImageData(0, 0, canvas.width, canvas.height).data;
    let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
    for (let y = 0; y < canvas.height; y++) {
        for (let x = 0; x < canvas.width; x++) {
            const alpha = imageData[(y * canvas.width + x) * 4 + 3];
            if (alpha > 0) {
                minX = Math.min(minX, x);
                minY = Math.min(minY, y);
                maxX = Math.max(maxX, x);
                maxY = Math.max(maxY, y);
            }
        }
    }
    return {
        left: minX,
        top: minY,
        width: maxX - minX,
        height: maxY - minY
    };
}

// you will need to create a new canvas
var canvas = document.createElement("canvas"),
    context = canvas.getContext("2d");

// and set the width and height the same as the canvas you want to draw the text
// here I'm just adding some values for the example:
canvas.width = 600;
canvas.height = 400;

// now I'm going to draw some text on it
context.font = "120px serif";
context.fillText("Lipsum", 100, 200);

let boundingBox = getVisiblePixelsBoundingBox(canvas);

// now this is not needed in your case, but want to see the canvas and
// I will draw a rectangle around the bounding box to see how it works:
document.body.appendChild(canvas);
context.strokeStyle = "red";
context.strokeRect(boundingBox.left, boundingBox.top, boundingBox.width, boundingBox.height);
Run Code Online (Sandbox Code Playgroud)

好的,这看起来不错,但要注意以下问题:

  • 精度仅限于像素,因此虽然绘制边界框完全可以,但对于需要更高精度的操作(例如完美对齐形状)来说并不好。另外,由于测量方法的原因,如果形状的一部分非常薄,根本不可见,那么它就不会被测量(对您来说可能不是一个大问题,因为如此极薄的形状不会发生在字体)。

  • 执行它可能会很慢,因为我们要迭代所有像素以获得结果(JS 对此并不是特别快)。知道这一点后,您可能会尝试减小画布大小,并且不使用与目标画布相同的画布,但我建议您不要这样做,因为这会引入与下一点相关的新问题。

  • 如果您在文本超出画布的情况下进行测量,则边界框将仅限于可见区域(它将粘在边框上)。如果这恰好获得了真实的宽度和高度,您可能会考虑增加画布大小,但如果您不限制字体大小和文本量,您可能会得到一个太大的画布,从而减慢浏览器的速度,或者可能甚至使其崩溃......所以我只能接受边界框粘在边界上。您可能认为使用measureText提供的宽度可能会有所帮助,但不幸的是,要获得顶部和高度,您需要扫描整个图像。