我们正在使用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提供的宽度可能会有所帮助,但不幸的是,要获得顶部和高度,您需要扫描整个图像。