合并画布代码

This commit is contained in:
X1627315083
2025-06-18 11:05:23 +08:00
parent 903c0ebdf5
commit 9c7fae36eb
118 changed files with 23633 additions and 8201 deletions

View File

@@ -1,4 +1,4 @@
//import { fabric } from "fabric-with-all";
import { fabric } from "fabric-with-all";
import initAligningGuidelines, {
initCenteringGuidelines,
} from "../utils/helperLine";
@@ -14,10 +14,15 @@ import { createCanvas } from "../utils/canvasFactory";
import { CanvasEventManager } from "./events/CanvasEventManager";
import CanvasConfig from "../config/canvasConfig";
import { RedGreenModeManager } from "./RedGreenModeManager";
import { EraserStateManager } from "./EraserStateManager";
import { deepClone, optimizeCanvasRendering } from "../utils/helper";
import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands";
import { isFunction } from "lodash-es";
import {
ChangeFixedImageCommand,
AddImageToLayerCommand,
} from "../commands/ObjectLayerCommands";
restoreObjectLayerAssociations,
simplifyLayers,
validateLayerAssociations,
} from "../utils/layerUtils";
export class CanvasManager {
constructor(canvasElement, options) {
@@ -34,6 +39,7 @@ export class CanvasManager {
this.canvasHeight = options.canvasHeight || this.height; // 画布高度
this.canvasColor = options.canvasColor || "#ffffff"; // 画布背景颜色
this.enabledRedGreenMode = options.enabledRedGreenMode || false; // 是否启用红绿图模式
this.eraserStateManager = null; // 橡皮擦状态管理器引用
// 初始化画布
this.initializeCanvas();
}
@@ -65,14 +71,6 @@ export class CanvasManager {
layers: this.layers,
});
// 初始化红绿图模式管理器
this.redGreenModeManager = new RedGreenModeManager({
canvas: this.canvas,
layerManager: null, // 稍后设置
toolManager: null, // 稍后设置
commandManager: null, // 稍后设置
});
// 设置画布辅助线
initAligningGuidelines(this.canvas);
@@ -90,7 +88,7 @@ export class CanvasManager {
*/
_initCanvasEvents() {
// 添加笔刷图像转换处理回调
this.canvas.onBrushImageConverted = (fabricImage) => {
this.canvas.onBrushImageConverted = async (fabricImage) => {
// 如果图层管理器存在,将图像合并到当前活动图层
if (this.layerManager) {
// 获取当前活动图层
@@ -107,21 +105,49 @@ export class CanvasManager {
});
// 执行高保真合并操作
this.eventManager?.mergeLayerObjectsForPerformance?.({
await this.eventManager?.mergeLayerObjectsForPerformance?.({
fabricImage,
activeLayer,
});
// 返回false表示不要自动添加到画布因为我们已经通过图层管理器处理了
return false;
// 返回true表示不要自动添加到画布因为我们已经通过图层管理器处理了
return true;
} else {
console.warn("没有活动图层,使用默认行为添加图像");
}
}
// 返回true表示使用默认行为直接添加到画布
return true;
// 返回false表示使用默认行为直接添加到画布
return false;
};
this.eraserStateManager = new EraserStateManager(
this.canvas,
this.layerManager
);
// 监听擦除开始事件
this.canvas.on("erasing:start", () => {
console.log("开始擦除");
this.eraserStateManager.startErasing();
});
// 监听擦除结束事件
this.canvas.on("erasing:end", async (e) => {
console.log("擦除完成", e.targets);
// 可以在这里保存状态到命令管理器
const affectedObjects = e.targets || [];
const command = this.eraserStateManager.endErasing(affectedObjects);
if (command && this.commandManager) {
await this.commandManager?.executeCommand?.(command);
} else {
await command?.execute?.(); // 如果没有命令管理器,直接执行命令
}
// 更新交互性
command &&
(await this.layerManager?.updateLayersObjectsInteractivity?.());
});
}
/**
@@ -168,6 +194,10 @@ export class CanvasManager {
if (this.redGreenModeManager) {
this.redGreenModeManager.layerManager = this.layerManager;
}
if (this.eraserStateManager) {
this.eraserStateManager.setLayerManager(this.layerManager);
}
}
/**
@@ -320,7 +350,8 @@ export class CanvasManager {
/**
* 居中所有画布元素
* 计算所有对象的边界框,然后将它们整体居中显示
* 以背景层为参照,计算背景层的偏移量并应用到所有对象上
* 这样可以保持对象间的相对位置关系不变
*/
centerAllObjects() {
if (!this.canvas) return;
@@ -333,45 +364,56 @@ export class CanvasManager {
(obj) => obj.visible !== false && !obj.excludeFromExport
);
// 如果只有背景层或没有可见对象,只居中背景层
if (
visibleObjects.length === 0 ||
(visibleObjects.length === 1 && visibleObjects[0].isBackground)
) {
// 尝试居中背景层
this.centerBackgroundLayer(this.width, this.height);
return;
}
// 如果没有可见对象,直接返回
if (visibleObjects.length === 0) return;
// 单独处理背景层
// 获取背景对象
const backgroundObject = visibleObjects.find((obj) => obj.isBackground);
const contentObjects = backgroundObject
? visibleObjects.filter((obj) => obj !== backgroundObject)
: visibleObjects;
// 如果只有背景层,居中背景层
if (contentObjects.length === 0 && backgroundObject) {
this.centerBackgroundLayer(this.width, this.height);
// 如果只有背景层或没有背景层,使用原有逻辑
if (!backgroundObject) {
console.warn("未找到背景层,使用默认居中逻辑");
// 如果只有一个对象且可能是背景,直接居中
if (visibleObjects.length === 1) {
const obj = visibleObjects[0];
obj.set({
left: this.width / 2,
top: this.height / 2,
originX: "center",
originY: "center",
});
obj.setCoords();
this.canvas.renderAll();
}
return;
}
// 计算内容对象的边界
const bounds = this._calculateObjectsBounds(contentObjects);
// 计算所有对象的中心点
const objectsCenterX = bounds.left + bounds.width / 2;
const objectsCenterY = bounds.top + bounds.height / 2;
// 记录背景层居中前的位置
const backgroundOldLeft = backgroundObject.left;
const backgroundOldTop = backgroundObject.top;
// 计算画布中心点
const canvasCenterX = this.width / 2;
const canvasCenterY = this.height / 2;
// 计算需要移动的距离
const deltaX = canvasCenterX - objectsCenterX;
const deltaY = canvasCenterY - objectsCenterY;
// 设置背景层居中
backgroundObject.set({
left: canvasCenterX,
top: canvasCenterY,
originX: "center",
originY: "center",
});
// 移动所有对象,包括背景层
visibleObjects.forEach((obj) => {
// 计算背景层的偏移量
const deltaX = backgroundObject.left - backgroundOldLeft;
const deltaY = backgroundObject.top - backgroundOldTop;
// 将相同的偏移量应用到所有其他对象上
const otherObjects = visibleObjects.filter(
(obj) => obj !== backgroundObject
);
otherObjects.forEach((obj) => {
obj.set({
left: obj.left + deltaX,
top: obj.top + deltaY,
@@ -456,6 +498,7 @@ export class CanvasManager {
// 如果需要裁剪背景层以外的内容,则更新蒙层位置
// 创建或更新蒙层
CanvasConfig.isCropBackground &&
!this.enabledRedGreenMode &&
this.createOrUpdateMask(backgroundLayerObject);
return true;
}
@@ -480,12 +523,13 @@ export class CanvasManager {
// 创建蒙层 - 使用透明矩形作为裁剪区域
this.maskLayer = new fabric.Rect({
id: "canvasMaskLayer",
width: bgWidth,
height: bgHeight,
left: left,
top: top,
fill: "transparent",
stroke: "#cccccc",
stroke: "transparent",
strokeWidth: 1,
strokeDashArray: [5, 5],
selectable: false,
@@ -504,20 +548,12 @@ export class CanvasManager {
this.canvas.clipPath = new fabric.Rect({
width: bgWidth,
height: bgHeight,
left: 0,
top: 0,
left: left,
top: top,
originX: backgroundLayerObject.originX || "left",
originY: backgroundLayerObject.originY || "top",
absolutePositioned: true,
});
// 设置蒙层位置
this.canvas.clipPath.set({
left: left,
top: top,
});
this.canvas.renderAll();
}
getBackgroundLayer() {
if (!this.canvas) return null;
@@ -655,6 +691,39 @@ export class CanvasManager {
});
}
/**
*
* 更改固定图层的图片
* @param {String} imageUrl 新的图片URL
* @param {Object} options 选项
* @param {String} options.targetLayerType 目标图层类型background/fixed
* @param {Boolean} options.undoable 是否可撤销,默认不可撤销
* @return {Object} 执行结果
* */
async changeFixedImage(imageUrl, options = {}) {
if (!this.layerManager) {
console.error("图层管理器未设置,无法更改固定图层图片");
return;
}
const command = new ChangeFixedImageCommand({
canvas: this.canvas,
layerManager: this.layerManager,
imageUrl: imageUrl,
targetLayerType: options.targetLayerType || "fixed", // background/fixed
});
command.undoable =
options.undoable !== undefined ? options.undoable : false; // 默认不可撤销 undoable = true 为可撤销
return (
(await command?.execute?.()) || {
success: false,
layerId: null,
imageUrl: null,
}
);
}
/**
* 导出图片
* @param {Object} options 导出选项
@@ -663,6 +732,7 @@ export class CanvasManager {
* @param {String} options.layerId 导出具体图层ID
* @param {Array} options.layerIdArray 导出多个图层ID数组
* @param {String} options.expPicType 导出图片类型 (png/jpg/svg)
* @param {Boolean} options.restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1
* @returns {String} 导出的图片数据URL
*/
exportImage(options = {}) {
@@ -672,7 +742,39 @@ export class CanvasManager {
}
try {
return this.exportManager.exportImage(options);
// 自动设置红绿图模式相关参数
const enhancedOptions = {
...options,
// 如果没有明确指定,则根据当前模式自动设置
restoreOpacityInRedGreen:
options.restoreOpacityInRedGreen !== undefined
? options.restoreOpacityInRedGreen
: true, // 默认在红绿图模式下恢复透明度
};
// 如果在红绿图模式下且没有指定具体的图层,自动包含所有普通图层
if (
this.enabledRedGreenMode &&
!options.layerId &&
(!options.layerIdArray || options.layerIdArray.length === 0)
) {
console.log("检测到红绿图模式,自动包含所有普通图层进行导出");
// 获取所有非背景、非固定的普通图层ID
const normalLayerIds =
this.layers?.value
?.filter(
(layer) => !layer.isBackground && !layer.isFixed && layer.visible
)
?.map((layer) => layer.id) || [];
if (normalLayerIds.length > 0) {
enhancedOptions.layerIdArray = normalLayerIds;
console.log("红绿图模式导出图层:", normalLayerIds);
}
}
return this.exportManager.exportImage(enhancedOptions);
} catch (error) {
console.error("CanvasManager导出图片失败:", error);
throw error;
@@ -709,129 +811,129 @@ export class CanvasManager {
}
getJSON() {
// 简化图层数据在loadJSON时要根据id恢复引用
let tempLayers = this.layers ? this.layers.value : [];
// // 简化图层数据在loadJSON时要根据id恢复引用
// let tempLayers = this.layers ? this.layers.value : [];
// // 创建对象ID映射表用于快速查找
// tempLayers = tempLayers.map((layer) => {
// const newLayer = { ...layer };
// 为所有fabric对象生成ID如果没有的话
const canvasObjects = this.canvas.getObjects();
canvasObjects.forEach((obj) => {
if (!obj.id) {
obj.id = `obj_${Date.now()}_${Math.floor(Math.random() * 10000)}`;
}
});
// // 处理fabricObjects数组
// if (Array.isArray(layer.fabricObjects)) {
// newLayer.fabricObjects = layer.fabricObjects
// .map((item) => {
// if (!item) return null;
// 创建对象ID映射表,用于快速查找
const objectIdMap = new Map();
canvasObjects.forEach((obj) => {
if (obj.id) {
objectIdMap.set(obj, obj.id);
}
});
// // 确保对象ID
// if (!item.id) {
// item.id = `obj_${Date.now()}_${Math.floor(
// Math.random() * 10000
// )}`;
// }
tempLayers = tempLayers.map((layer) => {
const newLayer = { ...layer };
// return {
// id: item.id,
// type: item.type || "object", // 保存类型信息用于调试
// };
// })
// .filter((item) => item !== null);
// } else {
// newLayer.fabricObjects = [];
// }
// 处理fabricObjects数组
if (Array.isArray(layer.fabricObjects)) {
newLayer.fabricObjects = layer.fabricObjects
.map((item) => {
if (!item) return null;
// if (layer.clippingMask) {
// layer.clippingMask = {
// id: layer.clippingMask.id,
// };
// }
// 确保对象有ID
if (!item.id) {
item.id = `obj_${Date.now()}_${Math.floor(
Math.random() * 10000
)}`;
}
// // 处理单个fabricObject
// if (layer.fabricObject) {
// if (!layer.fabricObject.id) {
// layer.fabricObject.id = `obj_${Date.now()}_${Math.floor(
// Math.random() * 10000
// )}`;
// }
// newLayer.fabricObject = {
// id: layer.fabricObject.id,
// type: layer.fabricObject.type || "object",
// };
// } else {
// newLayer.fabricObject = null;
// }
return {
id: item.id,
type: item.type || "object", // 保存类型信息用于调试
};
})
.filter((item) => item !== null);
} else {
newLayer.fabricObjects = [];
}
// // 处理子图层
// if (Array.isArray(layer.children)) {
// newLayer.children = layer.children.map((cItem) => {
// const newChild = { ...cItem };
// 处理单个fabricObject
if (layer.fabricObject) {
if (!layer.fabricObject.id) {
layer.fabricObject.id = `obj_${Date.now()}_${Math.floor(
Math.random() * 10000
)}`;
}
newLayer.fabricObject = {
id: layer.fabricObject.id,
type: layer.fabricObject.type || "object",
};
} else {
newLayer.fabricObject = null;
}
// // 处理子图层的fabricObjects
// if (Array.isArray(cItem.fabricObjects)) {
// newChild.fabricObjects = cItem.fabricObjects
// .map((item) => {
// if (!item) return null;
// 处理子图层
if (Array.isArray(layer.children)) {
newLayer.children = layer.children.map((cItem) => {
const newChild = { ...cItem };
// if (!item.id) {
// item.id = `obj_${Date.now()}_${Math.floor(
// Math.random() * 10000
// )}`;
// }
// 处理子图层的fabricObjects
if (Array.isArray(cItem.fabricObjects)) {
newChild.fabricObjects = cItem.fabricObjects
.map((item) => {
if (!item) return null;
// return {
// id: item.id,
// type: item.type || "object",
// };
// })
// .filter((item) => item !== null);
// } else {
// newChild.fabricObjects = [];
// }
if (!item.id) {
item.id = `obj_${Date.now()}_${Math.floor(
Math.random() * 10000
)}`;
}
// // 处理子图层的fabricObject
// if (cItem.fabricObject) {
// if (!cItem.fabricObject.id) {
// cItem.fabricObject.id = `obj_${Date.now()}_${Math.floor(
// Math.random() * 10000
// )}`;
// }
// newChild.fabricObject = {
// id: cItem.fabricObject.id,
// type: cItem.fabricObject.type || "object",
// };
// } else {
// newChild.fabricObject = null;
// }
return {
id: item.id,
type: item.type || "object",
};
})
.filter((item) => item !== null);
} else {
newChild.fabricObjects = [];
}
// 处理子图层的fabricObject
if (cItem.fabricObject) {
if (!cItem.fabricObject.id) {
cItem.fabricObject.id = `obj_${Date.now()}_${Math.floor(
Math.random() * 10000
)}`;
}
newChild.fabricObject = {
id: cItem.fabricObject.id,
type: cItem.fabricObject.type || "object",
};
} else {
newChild.fabricObject = null;
}
return newChild;
});
} else {
newLayer.children = [];
}
return newLayer;
});
// return newChild;
// });
// } else {
// newLayer.children = [];
// }
// return newLayer;
// });
try {
console.log(
"获取画布JSON数据...",
simplifyLayers(JSON.parse(JSON.stringify(this.layers.value)))
);
return JSON.stringify({
canvas: this.canvas.toJSON([
"id",
"type",
"layerId",
"layerName",
"isBackground",
"isLocked",
"isVisible",
"isFixed",
"parentId",
"excludeFromExport",
"eraser",
"eraserable",
"erasable",
]),
layers: tempLayers,
layers: JSON.stringify(
simplifyLayers(JSON.parse(JSON.stringify(this.layers.value)))
), // 简化图层数据
version: "1.0", // 添加版本信息
timestamp: new Date().toISOString(), // 添加时间戳
canvasWidth: this.canvasWidth.value,
@@ -845,7 +947,7 @@ export class CanvasManager {
}
}
loadJSON(json) {
loadJSON(json, calllBack) {
console.log("加载画布JSON数据:", json);
// 确保传入的json是字符串格式
@@ -859,7 +961,7 @@ export class CanvasManager {
const parsedJson = JSON.parse(json);
return new Promise((resolve, reject) => {
const tempLayers = parsedJson?.layers || [];
const tempLayers = JSON.parse(parsedJson?.layers) || [];
const canvasData = parsedJson?.canvas;
if (!tempLayers) {
@@ -872,9 +974,11 @@ export class CanvasManager {
return;
}
this.canvasWidth.value = parsedJson.canvasWidth || this.width;
this.canvasHeight.value = parsedJson.canvasHeight || this.height;
this.canvasColor.value = parsedJson.canvasColor || this.backgroundColor;
this.layers.value = tempLayers;
// this.canvasWidth.value = parsedJson.canvasWidth || this.width;
// this.canvasHeight.value = parsedJson.canvasHeight || this.height;
// this.canvasColor.value = parsedJson.canvasColor || this.backgroundColor;
console.log("是否检测到红绿图模式内容:", this.enabledRedGreenMode);
@@ -885,107 +989,57 @@ export class CanvasManager {
this.canvas.clear();
// 加载画布数据
this.canvas.loadFromJSON(canvasData, () => {
this.backgroundColor = parsedJson.backgroundColor || "#ffffff";
try {
// 重置画布数据
this.setCanvasSize(this.canvas.width, this.canvas.height);
this.canvas.loadFromJSON(canvasData, async () => {
await optimizeCanvasRendering(this.canvas, async () => {
this.backgroundColor = parsedJson.backgroundColor || "#ffffff";
try {
// 重置画布数据
this.setCanvasSize(this.canvas.width, this.canvas.height);
// 创建对象ID映射表用于快速查找
const objectIdMap = new Map();
const canvasObjects = this.canvas.getObjects();
// 重新构建对象关系
restoreObjectLayerAssociations(
this.layers.value,
this.canvas.getObjects()
);
canvasObjects.forEach((obj) => {
if (obj.id) {
objectIdMap.set(obj.id, obj);
}
});
// 验证图层关联关系 - 稳定后可以注释
const isValidate = validateLayerAssociations(
this.layers.value,
this.canvas.getObjects()
);
// 辅助函数根据ID查找对象
const findObjectById = (id) => {
if (!id) return null;
return objectIdMap.get(id) || null;
};
console.log("图层关联验证结果:", isValidate);
// 恢复图层数据
this.layers.value = tempLayers.map((layer) => {
const restoredLayer = { ...layer };
this.canvas.activeLayerId.value =
parsedJson?.activeLayerId || this.layers.value[0]?.id || null;
// 恢复fabricObjects数组
if (Array.isArray(layer.fabricObjects)) {
restoredLayer.fabricObjects = layer.fabricObjects
.map((item) => {
if (!item || !item.id) return null;
return findObjectById(item.id);
})
.filter((obj) => obj !== null);
} else {
restoredLayer.fabricObjects = [];
}
// // 如果检测到红绿图模式内容,进行缩放调整
// if (this.enabledRedGreenMode) {
// this._rescaleRedGreenModeContent();
// }
// 恢复单个fabricObject
if (layer.fabricObject && layer.fabricObject.id) {
restoredLayer.fabricObject = findObjectById(
layer.fabricObject.id
);
} else {
restoredLayer.fabricObject = null;
}
// 重载代码后支持回调中操作一些内容
await calllBack?.();
// 恢复子图层
if (Array.isArray(layer.children)) {
restoredLayer.children = layer.children.map((cItem) => {
const restoredChild = { ...cItem };
// 确保所有对象的交互性正确设置
await this.layerManager?.updateLayersObjectsInteractivity?.(
false
);
console.log(this.layerManager.layers.value);
debugger;
// 恢复子图层的fabricObjects
if (Array.isArray(cItem.fabricObjects)) {
restoredChild.fabricObjects = cItem.fabricObjects
.map((item) => {
if (!item || !item.id) return null;
return findObjectById(item.id);
})
.filter((obj) => obj !== null);
} else {
restoredChild.fabricObjects = [];
}
// 更新所有缩略图
setTimeout(() => {
this.updateAllThumbnails();
}, 100);
// 恢复子图层的fabricObject
if (cItem.fabricObject && cItem.fabricObject.id) {
restoredChild.fabricObject = findObjectById(
cItem.fabricObject.id
);
} else {
restoredChild.fabricObject = null;
}
return restoredChild;
});
} else {
restoredLayer.children = [];
}
return restoredLayer;
});
this.canvas.activeLayerId.value =
parsedJson?.activeLayerId || this.layers.value[0]?.id || null;
// 如果检测到红绿图模式内容,进行缩放调整
if (this.enabledRedGreenMode) {
this._rescaleRedGreenModeContent();
console.log("画布JSON数据加载完成");
resolve();
} catch (error) {
console.error("恢复图层数据失败:", error);
reject(new Error("恢复图层数据失败: " + error.message));
}
// 更新所有缩略图
setTimeout(() => {
this.updateAllThumbnails();
}, 100);
console.log("画布JSON数据加载完成");
resolve();
} catch (error) {
console.error("恢复图层数据失败:", error);
reject(new Error("恢复图层数据失败: " + error.message));
}
});
});
});
} catch (error) {