最近项目中使用Fabric.js做图像动态标记,被有些小问题缠住,花了很多时间去琢磨,有些甚至弄了一两天才理明白,特此记录。
1.canvas的width、height与style.width、style.height须一致,否则画出图形会变形或只显示图像的一部分。
canvasRef.value.width = img.naturalWidth;
canvasRef.value.height = img.naturalHeight;
canvasRef.value.style.width = img.naturalWidth + 'px';
canvasRef.value.style.height = img.naturalHeight + 'px';
canvasRef.value.width = img.naturalWidth;
canvasRef.value.height = img.naturalHeight;
canvasRef.value.style.width = img.naturalWidth + 'px';
canvasRef.value.style.height = img.naturalHeight + 'px';
canvasRef.value.width = img.naturalWidth;
canvasRef.value.height = img.naturalHeight;
canvasRef.value.style.width = img.naturalWidth + 'px';
canvasRef.value.style.height = img.naturalHeight + 'px';
看资料,fabric的画布实际上有三层(下图来自:jb51)

2.可以创建离屏/静态画布,不必在页面中显示,整体绘制完毕后再通过图像对象输出。
const canvas = new fabric.StaticCanvas(null, {
const canvas = new fabric.StaticCanvas(null, {
width: originWidth,
height: originHeight
});
const canvas = new fabric.StaticCanvas(null, {
width: originWidth,
height: originHeight
});
离屏画布的节点引用设置为null,一样可以设置宽高。
3. 如果有背景图片,所有创建的元素都需要在创建背景图片的函数体内完成,不能在函数体外创建,否则导出的图片将背景不可见。就是这个问题,我困扰了两天,开始以为是跨域问题,背景图片格式的问题等等,后来发现原因在此。
canvasRef.value.width = img.naturalWidth;
canvasRef.value.height = img.naturalHeight;
canvasRef.value.style.width = img.naturalWidth + 'px';
canvasRef.value.style.height = img.naturalHeight + 'px';
let currentCanvas = new fabric.Canvas(canvasRef.value, {
selectionColor: '#4fb8d3',
fabric.Image.fromURL(imgUrl, (img) => { //必须将绘制的文本和图形放到这个背景图函数体内,否则无法显示背景图
currentCanvas.setBackgroundImage(
currentCanvas.renderAll.bind(currentCanvas)
currentCanvas.backgroundImage.scaleToWidth(currentCanvas.width);
currentCanvas.backgroundImage.scaleToHeight(currentCanvas.height);
for (let key in LocationObj) {
let locationNum = LocationObj[key][0].bbox;
let arr = locationNum.split(',');
let numberArray = arr.map(Number);
let rect = new fabric.Rect({
width: numberArray[2] - numberArray[0],
height: numberArray[3] - numberArray[1],
stroke: 'rgb(235,64,64,0.9)',
let text = new fabric.Text(
Number(LocationObj[key][0].conf) * 100 +
left: numberArray[0], // 文本的X坐标
top: numberArray[1] - 22,
textBackgroundColor: '#eb4040', // 文字背景颜色
textBackgroundColor: 'rgb(235,64,64,0.8)',
hasControls: false, // 不显示控件
hasBorders: false, // 不显示边框
fontFamily: 'Arial,Microsoft YaHei'
currentCanvas.renderAll();
// rect.set('selectable', false);
// text.set('selectable', false);
downURL.value = currentCanvas.toDataURL({
downLink.value.download = 'X_RayImg.png';
{ crossOrigin: 'Anonymous' }
nextTick(
() => {
canvasRef.value.width = img.naturalWidth;
canvasRef.value.height = img.naturalHeight;
canvasRef.value.style.width = img.naturalWidth + 'px';
canvasRef.value.style.height = img.naturalHeight + 'px';
let currentCanvas = new fabric.Canvas(canvasRef.value, {
selectionColor: '#4fb8d3',
selectionLineWidth: 0
});
fabric.Image.fromURL(imgUrl, (img) => { //必须将绘制的文本和图形放到这个背景图函数体内,否则无法显示背景图
currentCanvas.setBackgroundImage(
img,
currentCanvas.renderAll.bind(currentCanvas)
);
currentCanvas.backgroundImage.scaleToWidth(currentCanvas.width);
currentCanvas.backgroundImage.scaleToHeight(currentCanvas.height);
for (let key in LocationObj) {
let locationNum = LocationObj[key][0].bbox;
let arr = locationNum.split(',');
let numberArray = arr.map(Number);
let rect = new fabric.Rect({
left: numberArray[0],
top: numberArray[1],
width: numberArray[2] - numberArray[0],
height: numberArray[3] - numberArray[1],
fill: 'transparent',
stroke: 'rgb(235,64,64,0.9)',
strokeWidth: 3,
strokeDashArray: [5, 5]
});
let text = new fabric.Text(
' ' +
messageMap.value[key] +
' - ' +
Number(LocationObj[key][0].conf) * 100 +
'%' +
' ',
{
left: numberArray[0], // 文本的X坐标
top: numberArray[1] - 22,
fontSize: 22, // 字体大小
textBackgroundColor: '#eb4040', // 文字背景颜色
textBackgroundColor: 'rgb(235,64,64,0.8)',
hasControls: false, // 不显示控件
hasBorders: false, // 不显示边框
fontWeight: 'normal',
fill: 'white',
padding: 20,
lineHeight: 3,
textAlign: 'right',
fontFamily: 'Arial,Microsoft YaHei'
}
);
currentCanvas.add(rect);
currentCanvas.add(text);
currentCanvas.renderAll();
// rect.set('selectable', false);
// text.set('selectable', false);
}
downURL.value = currentCanvas.toDataURL({
format: 'png',
quality: 0.9
});
downLink.value.download = 'X_RayImg.png';
});
},
{ crossOrigin: 'Anonymous' }
);
nextTick(
() => {
canvasRef.value.width = img.naturalWidth;
canvasRef.value.height = img.naturalHeight;
canvasRef.value.style.width = img.naturalWidth + 'px';
canvasRef.value.style.height = img.naturalHeight + 'px';
let currentCanvas = new fabric.Canvas(canvasRef.value, {
selectionColor: '#4fb8d3',
selectionLineWidth: 0
});
fabric.Image.fromURL(imgUrl, (img) => { //必须将绘制的文本和图形放到这个背景图函数体内,否则无法显示背景图
currentCanvas.setBackgroundImage(
img,
currentCanvas.renderAll.bind(currentCanvas)
);
currentCanvas.backgroundImage.scaleToWidth(currentCanvas.width);
currentCanvas.backgroundImage.scaleToHeight(currentCanvas.height);
for (let key in LocationObj) {
let locationNum = LocationObj[key][0].bbox;
let arr = locationNum.split(',');
let numberArray = arr.map(Number);
let rect = new fabric.Rect({
left: numberArray[0],
top: numberArray[1],
width: numberArray[2] - numberArray[0],
height: numberArray[3] - numberArray[1],
fill: 'transparent',
stroke: 'rgb(235,64,64,0.9)',
strokeWidth: 3,
strokeDashArray: [5, 5]
});
let text = new fabric.Text(
' ' +
messageMap.value[key] +
' - ' +
Number(LocationObj[key][0].conf) * 100 +
'%' +
' ',
{
left: numberArray[0], // 文本的X坐标
top: numberArray[1] - 22,
fontSize: 22, // 字体大小
textBackgroundColor: '#eb4040', // 文字背景颜色
textBackgroundColor: 'rgb(235,64,64,0.8)',
hasControls: false, // 不显示控件
hasBorders: false, // 不显示边框
fontWeight: 'normal',
fill: 'white',
padding: 20,
lineHeight: 3,
textAlign: 'right',
fontFamily: 'Arial,Microsoft YaHei'
}
);
currentCanvas.add(rect);
currentCanvas.add(text);
currentCanvas.renderAll();
// rect.set('selectable', false);
// text.set('selectable', false);
}
downURL.value = currentCanvas.toDataURL({
format: 'png',
quality: 0.9
});
downLink.value.download = 'X_RayImg.png';
});
},
{ crossOrigin: 'Anonymous' }
);