我正在开发一个在 Fabric.js 画布上进行图像处理的 Rails 应用程序,我想实现一个语音气泡,就像这篇文章中的那样:Canvas/FabricJS 中的语音气泡?。其中一个答案给出了一段 Javascript 代码,该代码只需进行最少的更改即可正常工作,直到我尝试将对画布中的图像所做的更改保存起来。对话气泡由 5 个元素组成:一个文本框、一个矩形、2 个多边形和一个手柄,所有元素都相互关联显示,其中 2 个多边形能够独立于矩形和文本框移动。当我将对话气泡放置在画布上的某个位置后保存更改时,元素不会保存彼此之间的关系,而是每个元素都会保存为唯一的画布对象,并且它们会丢失其原始位置。
我尝试将它们组合在一起,但这使得您无法独立移动多边形,并且通常会破坏语音气泡布局。我真正需要的是在其中一个元素中有一个事件侦听器来切换语音气泡的显示,但当我尝试这样做时,我遇到了相同的保存问题。在同一篇文章的另一个答案中,另一位用户表示他们在存储和加载画布数据时遇到问题,他们提供了一个解决方案,但我无法成功实现它,因为我对保存方法所做的任何更改都会使画布完全无响应,并且已经存在的所有其他不相关对象永远不会显示。
function saveCanvas(canvas, formInputField) {
delete canvas.backgroundImage;
formInputField.value = JSON.stringify(canvas);
}
function initializeFabricCanvas(canvasContainer, backgroundImageElement, canvasAsJSON) {
let canvas = new fabric.Canvas(canvasContainer.id);
enableHighlightDestroy(canvas);
enableZoom(canvas);
enablePan(canvas);
enableNumber(canvas);
enableComment(canvas);
loadSavedCanvas(canvas, canvasAsJSON);
let canvasSize = loadImageIntoCanvas(canvas, backgroundImageElement);
resizeCanvasToImage(canvasContainer, canvas, canvasSize);
document.getElementById("addCommentButton").addEventListener("click", function () {
console.log("addCommentButton clicked");
enableComment(canvas);
});
return canvas;
}
function loadSavedCanvas(canvas, canvasAsJSON) {
if (canvasAsJSON.trim() !== "") {
canvas.loadFromJSON(canvasAsJSON);
}
}
function enableComment(canvas) {
var boxPadding = 16;
var arrowWidth = 16;
var strokeWidth = 2;
var handleSize = 24;
var textbox = new fabric.Textbox("Your comment text goes here", {
left: 200,
top: 80,
width: 180,
fontSize: 16,
originY: "center",
originX: "center",
});
var setCoords = textbox.setCoords.bind(textbox);
textbox.on({
moving: setCoords,
scaling: setCoords,
rotating: setCoords,
});
textbox.lastLeft = textbox.left;
textbox.lastTop = textbox.top;
var handle = new fabric.Rect({
fill: "transparent",
left: 128,
top: 160,
width: handleSize,
height: handleSize,
hasRotatingPoint: false,
hasControls: false,
originY: "center",
originX: "center",
});
var rect = new fabric.Rect({
fill: "white",
stroke: "black",
strokeWidth: strokeWidth,
rx: 8,
ry: 8,
objectCaching: false,
});
var poly = new fabric.Polygon(
[
{ x: 0, y: 0 },
{ x: 1, y: 1 },
{ x: 1, y: 0 },
],
{
fill: "white",
stroke: "black",
strokeWidth: strokeWidth,
objectCaching: false,
}
);
var poly2 = new fabric.Polygon(
[
{ x: 0, y: 0 },
{ x: 1, y: 1 },
{ x: 1, y: 0 },
],
{
fill: "white",
objectCaching: false,
}
);
// Create an asterisk at the tip of the handle
var asterisk = new fabric.Text('*', {
fontSize: 26,
fill: 'red',
originX: 'center',
originY: 'bottom',
selectable: false,
evented: false,
});
canvas.add(poly, rect, poly2, textbox, asterisk); // Add the asterisk to the canvas
canvas.add(handle).setActiveObject(handle);
canvas.on("after:render", updateBubble);
updateBubble();
function updateBubble() {
console.log("Updating Bubble...");
var x = textbox.left;
var y = textbox.top;
var bound = textbox.getBoundingRect();
rect.left = bound.left - boxPadding;
rect.top = bound.top - boxPadding;
rect.width = bound.width + boxPadding * 2;
rect.height = bound.height + boxPadding * 2;
if (x !== textbox.lastLeft || y !== textbox.lastTop) {
handle.left += x - textbox.lastLeft;
handle.top += y - textbox.lastTop;
handle.setCoords();
asterisk.left += x - textbox.lastLeft; // Move the asterisk with the handle
asterisk.top += y - textbox.lastTop;
asterisk.setCoords();
}
var halfPi = Math.PI / 2;
var angleRadians = Math.atan2(handle.top - y, handle.left - x);
var offsetX = Math.cos(angleRadians + halfPi);
var offsetY = Math.sin(angleRadians + halfPi);
poly.points[0].x = handle.left;
poly.points[0].y = handle.top;
poly.points[1].x = x - offsetX * arrowWidth;
poly.points[1].y = y - offsetY * arrowWidth;
poly.points[2].x = x + offsetX * arrowWidth;
poly.points[2].y = y + offsetY * arrowWidth;
var halfStroke = strokeWidth / 2;
poly2.points[0].x = handle.left;
poly2.points[0].y = handle.top;
poly2.points[1].x = x - offsetX * (arrowWidth - halfStroke);
poly2.points[1].y = y - offsetY * (arrowWidth - halfStroke);
poly2.points[2].x = x + offsetX * (arrowWidth - halfStroke);
poly2.points[2].y = y + offsetY * (arrowWidth - halfStroke);
// Position the asterisk at the tip of the handle
asterisk.left = handle.left;
asterisk.top = handle.top - handle.height / 2;
// Adjust the position with offsets
var asteriskOffsetX = 0; // adjust as needed
var asteriskOffsetY = 32; // adjust as needed
asterisk.left += asteriskOffsetX;
asterisk.top += asteriskOffsetY;
textbox.lastLeft = x;
textbox.lastTop = y;
}
}
星号是我尝试添加用于显示切换的事件侦听器的位置,但我无法首先保存语音气泡并使其发挥作用。
如果有人可以提供建议,那将是一个巨大的帮助。抱歉,如果这篇文章是多余的,但即使在其他人的帮助下,我也无法实现保存和加载解决方案。
idk你的fabricJS版本,但你可以尝试像这样保存你的画布:
canvas.toObject(propertiesToInclude)
propertiesToIninclude 作为您需要导出的不同(默认或自定义)属性的数组:
export const propertiesToInclude = [
'id',
'name',
'type',
'isShow',
'isCheck',
'color',
'axis',
'cropKey',
'cropPath',
'cropSize',
'fill',
'selectable',
'evented',
'fillType',
'fillURL',
'fillRepeat',
'lockMovementX',
// Form mode
'isForm',
// workspace
'workspace',
//other
'_objectFeatures'
]