feat: 裁剪组裁剪跟随选择组移动

This commit is contained in:
bighuixiang
2025-07-14 01:00:23 +08:00
parent 96e13cb22a
commit 24e9ba8ae5
80 changed files with 2052 additions and 4292 deletions

View File

@@ -22,9 +22,7 @@ export class CreateBackgroundLayerCommand extends Command {
execute() {
// 检查是否已经存在背景图层
const existingBgLayer = this.layers.value.find(
(layer) => layer.isBackground
);
const existingBgLayer = this.layers.value.find((layer) => layer.isBackground);
if (existingBgLayer) {
console.warn("已存在背景层,不重复创建");
return existingBgLayer.id;
@@ -34,11 +32,7 @@ export class CreateBackgroundLayerCommand extends Command {
const bgObject = this._createBackgroundObject();
// 将背景对象添加到图层中
this.backgroundLayer.fabricObject = bgObject.toObject([
"id",
"layerId",
"type",
]);
this.backgroundLayer.fabricObject = bgObject.toObject(["id", "layerId", "type"]);
// 添加图层到最底部
this.layers.value.push(this.backgroundLayer);
@@ -54,9 +48,7 @@ export class CreateBackgroundLayerCommand extends Command {
undo() {
// 从图层列表中删除背景图层
const bgLayerIndex = this.layers.value.findIndex(
(layer) => layer.isBackground
);
const bgLayerIndex = this.layers.value.findIndex((layer) => layer.isBackground);
if (bgLayerIndex !== -1) {
this.layers.value.splice(bgLayerIndex, 1);
}
@@ -82,8 +74,7 @@ export class CreateBackgroundLayerCommand extends Command {
// 确保背景色为白色,如果没有设置或者是透明的话
const backgroundColor =
this.backgroundLayer.backgroundColor &&
this.backgroundLayer.backgroundColor !== "transparent"
this.backgroundLayer.backgroundColor && this.backgroundLayer.backgroundColor !== "transparent"
? this.backgroundLayer.backgroundColor
: "#ffffff";
@@ -139,10 +130,7 @@ export class UpdateBackgroundCommand extends Command {
// 查找背景图层
this.bgLayer = this.layers.value.find((layer) => layer.isBackground);
this.oldBackgroundColor = this.oldColor;
this.backgroundObject = findObjectById(
this.canvas,
this.bgLayer.fabricObject.id
).object;
this.backgroundObject = findObjectById(this.canvas, this.bgLayer.fabricObject.id).object;
}
execute() {
@@ -163,9 +151,7 @@ export class UpdateBackgroundCommand extends Command {
this.backgroundColorValue.value = this.backgroundColor; // 设置背景颜色
// 生成缩略图
this.canvasManager?.thumbnailManager?.generateLayerThumbnail?.(
this.bgLayer.id
);
this.canvasManager?.thumbnailManager?.generateLayerThumbnail?.(this.bgLayer.id);
return true;
}
@@ -186,9 +172,7 @@ export class UpdateBackgroundCommand extends Command {
this.backgroundColorValue.value = this.oldBackgroundColor; // 恢复背景颜色
// 生成缩略图
this.canvasManager?.thumbnailManager?.generateLayerThumbnail?.(
this.bgLayer.id
);
this.canvasManager?.thumbnailManager?.generateLayerThumbnail?.(this.bgLayer.id);
// 如果有旧颜色,恢复到旧颜色
return true;
}
@@ -223,10 +207,7 @@ export class BackgroundSizeCommand extends Command {
this.bgLayer = this.layers.value.find((layer) => layer.isBackground);
// 记录原尺寸
this.backgroundObject = findObjectById(
this.canvas,
this.bgLayer.fabricObject.id
).object;
this.backgroundObject = findObjectById(this.canvas, this.bgLayer.fabricObject.id).object;
this.oldWidth = this.backgroundObject.width;
this.oldHeight = this.backgroundObject.height;
@@ -255,8 +236,7 @@ export class BackgroundSizeCommand extends Command {
// 调整背景对象大小
if (this.bgLayer && this.backgroundObject) {
// 保持原有的背景颜色,如果没有设置则使用白色
const currentFill =
this.backgroundObject.fill || this.bgLayer.backgroundColor || "#ffffff";
const currentFill = this.backgroundObject.fill || this.bgLayer.backgroundColor || "#ffffff";
this.backgroundObject.set({
width: this.newWidth,
@@ -343,10 +323,7 @@ export class BackgroundSizeWithScaleCommand extends Command {
// 查找背景图层
this.bgLayer = this.layers.value.find((layer) => layer.isBackground);
this.backgroundObject = findObjectById(
this.canvas,
this.bgLayer.fabricObject.id
).object;
this.backgroundObject = findObjectById(this.canvas, this.bgLayer.fabricObject.id).object;
// 计算缩放比例
const scaleXRatio = this.newWidth / this.oldWidth;
@@ -409,24 +386,21 @@ export class BackgroundSizeWithScaleCommand extends Command {
// 统一缩放:使用平均值,保持相对比例的同时允许适度的形变
this.uniformScale = Math.sqrt(scaleXRatio * scaleYRatio);
this.offsetX = (this.newWidth - this.oldWidth * this.uniformScale) / 2;
this.offsetY =
(this.newHeight - this.oldHeight * this.uniformScale) / 2;
this.offsetY = (this.newHeight - this.oldHeight * this.uniformScale) / 2;
break;
case "fit":
// 适应模式:使用较小值,确保所有内容都在画布内,可能有留白
this.uniformScale = Math.min(scaleXRatio, scaleYRatio);
this.offsetX = (this.newWidth - this.oldWidth * this.uniformScale) / 2;
this.offsetY =
(this.newHeight - this.oldHeight * this.uniformScale) / 2;
this.offsetY = (this.newHeight - this.oldHeight * this.uniformScale) / 2;
break;
case "fill":
// 填充模式:使用较大值,填满画布,可能有部分内容被裁切
this.uniformScale = Math.max(scaleXRatio, scaleYRatio);
this.offsetX = (this.newWidth - this.oldWidth * this.uniformScale) / 2;
this.offsetY =
(this.newHeight - this.oldHeight * this.uniformScale) / 2;
this.offsetY = (this.newHeight - this.oldHeight * this.uniformScale) / 2;
break;
case "stretch":
@@ -441,8 +415,7 @@ export class BackgroundSizeWithScaleCommand extends Command {
// 默认使用uniform模式
this.uniformScale = Math.sqrt(scaleXRatio * scaleYRatio);
this.offsetX = (this.newWidth - this.oldWidth * this.uniformScale) / 2;
this.offsetY =
(this.newHeight - this.oldHeight * this.uniformScale) / 2;
this.offsetY = (this.newHeight - this.oldHeight * this.uniformScale) / 2;
}
}
@@ -462,8 +435,7 @@ export class BackgroundSizeWithScaleCommand extends Command {
// 调整背景对象大小和位置
if (this.bgLayer && this.backgroundObject) {
// 保持原有的背景颜色,如果没有设置则使用白色
const currentFill =
this.backgroundObject.fill || this.bgLayer.backgroundColor || "#ffffff";
const currentFill = this.backgroundObject.fill || this.bgLayer.backgroundColor || "#ffffff";
this.backgroundObject.set({
width: this.newWidth,
@@ -480,12 +452,10 @@ export class BackgroundSizeWithScaleCommand extends Command {
// 计算基于原始画布的缩放比例
const baseScaleX =
this.newWidth /
this.objectStates[0]?.obj._originalState?.baseCanvasWidth ||
this.newWidth / this.objectStates[0]?.obj._originalState?.baseCanvasWidth ||
this.newWidth / this.oldWidth;
const baseScaleY =
this.newHeight /
this.objectStates[0]?.obj._originalState?.baseCanvasHeight ||
this.newHeight / this.objectStates[0]?.obj._originalState?.baseCanvasHeight ||
this.newHeight / this.oldHeight;
// 根据策略缩放所有非背景对象
@@ -505,13 +475,11 @@ export class BackgroundSizeWithScaleCommand extends Command {
const baseUniformScale = Math.sqrt(baseScaleX * baseScaleY);
const baseOffsetX =
(this.newWidth -
(obj._originalState?.baseCanvasWidth || this.oldWidth) *
baseUniformScale) /
(obj._originalState?.baseCanvasWidth || this.oldWidth) * baseUniformScale) /
2;
const baseOffsetY =
(this.newHeight -
(obj._originalState?.baseCanvasHeight || this.oldHeight) *
baseUniformScale) /
(obj._originalState?.baseCanvasHeight || this.oldHeight) * baseUniformScale) /
2;
obj.set({
@@ -537,10 +505,7 @@ export class BackgroundSizeWithScaleCommand extends Command {
this.canvas.setHeight(this.oldHeight);
// 如果使用 CanvasManager通知它画布大小恢复
if (
this.canvasManager &&
typeof this.canvasManager.updateCanvasSize === "function"
) {
if (this.canvasManager && typeof this.canvasManager.updateCanvasSize === "function") {
this.canvasManager.updateCanvasSize(this.oldWidth, this.oldHeight);
}
@@ -568,12 +533,9 @@ export class BackgroundSizeWithScaleCommand extends Command {
const baseScaleX = this.oldWidth / originalState.baseCanvasWidth;
const baseScaleY = this.oldHeight / originalState.baseCanvasHeight;
const baseUniformScale = Math.sqrt(baseScaleX * baseScaleY);
const baseOffsetX =
(this.oldWidth - originalState.baseCanvasWidth * baseUniformScale) /
2;
const baseOffsetX = (this.oldWidth - originalState.baseCanvasWidth * baseUniformScale) / 2;
const baseOffsetY =
(this.oldHeight - originalState.baseCanvasHeight * baseUniformScale) /
2;
(this.oldHeight - originalState.baseCanvasHeight * baseUniformScale) / 2;
obj.set({
left: originalState.left * baseUniformScale + baseOffsetX,

View File

@@ -26,9 +26,7 @@ export class BrushSizeCommand extends BaseBrushCommand {
super({
...options,
name: `设置笔刷大小: ${options.size}`,
description: `将笔刷大小从 ${options.previousSize || "?"} 设为 ${
options.size
}`,
description: `将笔刷大小从 ${options.previousSize || "?"} 设为 ${options.size}`,
});
this.size = options.size;
@@ -66,9 +64,7 @@ export class BrushColorCommand extends BaseBrushCommand {
super({
...options,
name: `设置笔刷颜色: ${options.color}`,
description: `将笔刷颜色从 ${options.previousColor || "?"} 设为 ${
options.color
}`,
description: `将笔刷颜色从 ${options.previousColor || "?"} 设为 ${options.color}`,
});
this.color = options.color;
@@ -106,14 +102,11 @@ export class BrushOpacityCommand extends BaseBrushCommand {
super({
...options,
name: `设置笔刷透明度: ${options.opacity}`,
description: `将笔刷透明度从 ${options.previousOpacity || "?"} 设为 ${
options.opacity
}`,
description: `将笔刷透明度从 ${options.previousOpacity || "?"} 设为 ${options.opacity}`,
});
this.opacity = options.opacity;
this.previousOpacity =
options.previousOpacity || this.brushStore.state.opacity;
this.previousOpacity = options.previousOpacity || this.brushStore.state.opacity;
}
execute() {
@@ -147,9 +140,7 @@ export class BrushTypeCommand extends BaseBrushCommand {
super({
...options,
name: `设置笔刷类型: ${options.brushType}`,
description: `将笔刷类型从 ${options.previousType || "?"} 设为 ${
options.brushType
}`,
description: `将笔刷类型从 ${options.previousType || "?"} 设为 ${options.brushType}`,
});
this.brushType = options.brushType;
@@ -192,9 +183,7 @@ export class TextureCommand extends BaseBrushCommand {
super({
...options,
name: options.enabled ? "启用笔刷材质" : "禁用笔刷材质",
description: options.enabled
? `启用材质: ${options.path || "[默认]"}`
: "禁用笔刷材质",
description: options.enabled ? `启用材质: ${options.path || "[默认]"}` : "禁用笔刷材质",
});
this.enabled = options.enabled;
@@ -260,9 +249,7 @@ export class BrushPresetCommand extends BaseBrushCommand {
*/
constructor(options = {}) {
const presetName =
typeof options.preset === "object"
? options.preset.name
: `预设 ${options.preset}`;
typeof options.preset === "object" ? options.preset.name : `预设 ${options.preset}`;
super({
...options,
@@ -303,14 +290,10 @@ export class BrushPresetCommand extends BaseBrushCommand {
this.brushStore.applyPreset(this.preset);
} else if (typeof this.preset === "object") {
// 应用自定义预设对象
if (this.preset.size !== undefined)
this.brushStore.setBrushSize(this.preset.size);
if (this.preset.color !== undefined)
this.brushStore.setBrushColor(this.preset.color);
if (this.preset.opacity !== undefined)
this.brushStore.setBrushOpacity(this.preset.opacity);
if (this.preset.type !== undefined)
this.brushStore.setBrushType(this.preset.type);
if (this.preset.size !== undefined) this.brushStore.setBrushSize(this.preset.size);
if (this.preset.color !== undefined) this.brushStore.setBrushColor(this.preset.color);
if (this.preset.opacity !== undefined) this.brushStore.setBrushOpacity(this.preset.opacity);
if (this.preset.type !== undefined) this.brushStore.setBrushType(this.preset.type);
if (this.preset.textureEnabled !== undefined) {
this.brushStore.setTextureEnabled(this.preset.textureEnabled);
@@ -542,8 +525,7 @@ export class TexturePresetCommand extends BaseBrushCommand {
* @param {Object} options.brushManager 笔刷管理器实例
*/
constructor(options = {}) {
const presetName =
typeof options.preset === "object" ? options.preset.name : options.preset;
const presetName = typeof options.preset === "object" ? options.preset.name : options.preset;
super({
...options,
@@ -614,10 +596,7 @@ export class TextureUploadCommand extends BaseBrushCommand {
});
this.file = options.file;
this.name =
options.name ||
options.file?.name?.replace(/\.[^/.]+$/, "") ||
"自定义纹理";
this.name = options.name || options.file?.name?.replace(/\.[^/.]+$/, "") || "自定义纹理";
this.category = options.category || "自定义材质";
this.texturePresetManager = options.texturePresetManager;
this.brushManager = options.brushManager;

View File

@@ -24,8 +24,7 @@ export class ClearSelectionContentCommand extends Command {
this.oldLayer = [...this.layers.value]; // 获取原图层对象
const { layer } =
findLayerRecursively(this.layers.value, this.targetLayerId) || {};
const { layer } = findLayerRecursively(this.layers.value, this.targetLayerId) || {};
// 栅格化相关属性
this.originalLayerBackup = null;
@@ -37,17 +36,10 @@ export class ClearSelectionContentCommand extends Command {
// this.originalLayerObject = fabric.util.object.clone(originalLayerObject); // 获取原图层对象
this.oldLayerObjects = originalLayerObject.toObject([
"id",
"layerId",
"layerName",
]); // 获取原图层的所有对象
this.oldLayerObjects = originalLayerObject.toObject(["id", "layerId", "layerName"]); // 获取原图层的所有对象
this.rasterizedImage = null;
this.targetLayer = findLayerRecursively(
this.canvas,
this.targetLayerId
)?.layer;
this.targetLayer = findLayerRecursively(this.canvas, this.targetLayerId)?.layer;
this.layerRasterized = false;
@@ -88,8 +80,7 @@ export class ClearSelectionContentCommand extends Command {
}
// 确定目标图层
const layerId =
this.targetLayerId || this.layerManager.getActiveLayerId();
const layerId = this.targetLayerId || this.layerManager.getActiveLayerId();
this.targetLayer = this.layerManager.getLayerById(layerId);
if (!this.targetLayer) {
console.error("无法清除选区内容:目标图层无效");
@@ -107,9 +98,7 @@ export class ClearSelectionContentCommand extends Command {
}
// 创建反转选区(保留选区外的内容)
const invertedSelection = await this._createInvertedSelection(
this.selectionObject
);
const invertedSelection = await this._createInvertedSelection(this.selectionObject);
if (!invertedSelection) {
console.error("创建反转选区失败");
return false;
@@ -137,10 +126,7 @@ export class ClearSelectionContentCommand extends Command {
// 恢复原图层状态
// 移除栅格化后的图像
if (
this.rasterizedImage &&
this.canvas.getObjects().includes(this.rasterizedImage)
) {
if (this.rasterizedImage && this.canvas.getObjects().includes(this.rasterizedImage)) {
this.canvas.remove(this.rasterizedImage);
}
@@ -199,10 +185,7 @@ export class ClearSelectionContentCommand extends Command {
let scaleFactor = this.baseResolutionScale;
if (this.highResolutionEnabled) {
const currentZoom = this.canvas.getZoom?.() || 1;
scaleFactor = Math.max(
scaleFactor || this.canvas?.getRetinaScaling?.(),
currentZoom
);
scaleFactor = Math.max(scaleFactor || this.canvas?.getRetinaScaling?.(), currentZoom);
scaleFactor = Math.min(scaleFactor, 3);
}
@@ -286,9 +269,7 @@ export class ClearSelectionContentCommand extends Command {
layerBounds.left + layerBounds.width
} ${layerBounds.top} L ${layerBounds.left + layerBounds.width} ${
layerBounds.top + layerBounds.height
} L ${layerBounds.left} ${layerBounds.top + layerBounds.height} Z ${
selectionObject.path
}`;
} L ${layerBounds.left} ${layerBounds.top + layerBounds.height} Z ${selectionObject.path}`;
const invertedPath = new fabric.Path(pathString, {
fillRule: "evenodd",
@@ -329,14 +310,8 @@ export class ClearSelectionContentCommand extends Command {
if (!bounds) {
bounds = { ...objBounds };
} else {
const right = Math.max(
bounds.left + bounds.width,
objBounds.left + objBounds.width
);
const bottom = Math.max(
bounds.top + bounds.height,
objBounds.top + objBounds.height
);
const right = Math.max(bounds.left + bounds.width, objBounds.left + objBounds.width);
const bottom = Math.max(bounds.top + bounds.height, objBounds.top + objBounds.height);
bounds.left = Math.min(bounds.left, objBounds.left);
bounds.top = Math.min(bounds.top, objBounds.top);
@@ -370,15 +345,10 @@ export class ClearSelectionContentCommand extends Command {
// 递归获取图层及其子图层的所有对象
const collectLayerObjects = (currentLayer) => {
// 处理图层的fabricObjects
if (
currentLayer.fabricObjects &&
Array.isArray(currentLayer.fabricObjects)
) {
if (currentLayer.fabricObjects && Array.isArray(currentLayer.fabricObjects)) {
currentLayer.fabricObjects.forEach((fabricObj) => {
if (fabricObj && fabricObj.id) {
const realObject = canvasObjects.find(
(canvasObj) => canvasObj.id === fabricObj.id
);
const realObject = canvasObjects.find((canvasObj) => canvasObj.id === fabricObj.id);
if (realObject && realObject.visible !== false) {
objects.push(realObject);
}
@@ -420,16 +390,12 @@ export class ClearSelectionContentCommand extends Command {
}
// 移除栅格化后的图像
if (
this.rasterizedImage &&
this.canvas.getObjects().includes(this.rasterizedImage)
) {
if (this.rasterizedImage && this.canvas.getObjects().includes(this.rasterizedImage)) {
this.canvas.remove(this.rasterizedImage);
}
// 恢复图层的fabricObjects
this.targetLayer.fabricObjects =
this.originalLayerBackup.fabricObjects || [];
this.targetLayer.fabricObjects = this.originalLayerBackup.fabricObjects || [];
// 重新创建并添加对象到画布
if (this.targetLayer.fabricObjects.length > 0) {

View File

@@ -109,10 +109,7 @@ export class CompositeCommand extends Command {
console.log(`✅ 子命令执行成功: ${command.constructor.name}`);
} catch (error) {
console.error(
`❌ 子命令执行失败: ${command.constructor.name}`,
error
);
console.error(`❌ 子命令执行失败: ${command.constructor.name}`, error);
// 执行失败时,撤销已执行的命令
await this._rollbackExecutedCommands();
@@ -142,9 +139,7 @@ export class CompositeCommand extends Command {
return true;
}
console.log(
`↩️ 开始撤销复合命令,共 ${this.executedCommands.length} 个子命令`
);
console.log(`↩️ 开始撤销复合命令,共 ${this.executedCommands.length} 个子命令`);
try {
// 逆序撤销已执行的命令
@@ -164,10 +159,7 @@ export class CompositeCommand extends Command {
results.push(finalResult);
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
} catch (error) {
console.error(
`❌ 子命令撤销失败: ${command.constructor.name}`,
error
);
console.error(`❌ 子命令撤销失败: ${command.constructor.name}`, error);
// 撤销失败不中断整个撤销过程,但要记录错误
}
} else {
@@ -203,10 +195,7 @@ export class CompositeCommand extends Command {
}
console.log(`✅ 子命令回滚成功: ${command.constructor.name}`);
} catch (error) {
console.error(
`❌ 子命令回滚失败: ${command.constructor.name}`,
error
);
console.error(`❌ 子命令回滚失败: ${command.constructor.name}`, error);
// 回滚失败不中断整个回滚过程
}
}
@@ -238,9 +227,7 @@ export class CompositeCommand extends Command {
commandCount: this.commands.length,
executedCount: this.executedCommands.length,
isExecuting: this.isExecuting,
subCommands: this.commands.map((cmd) =>
cmd.getInfo ? cmd.getInfo() : cmd.constructor.name
),
subCommands: this.commands.map((cmd) => (cmd.getInfo ? cmd.getInfo() : cmd.constructor.name)),
};
}
}

View File

@@ -85,9 +85,7 @@ export class CrossLevelMoveCommand extends Command {
this.layers.value.forEach((layer) => {
if (layer.fabricObject) {
states[layer.id] = {
isInCanvas: this.canvas
? this.canvas.contains(layer.fabricObject)
: false,
isInCanvas: this.canvas ? this.canvas.contains(layer.fabricObject) : false,
parentGroupId: null,
visible: layer.fabricObject.visible,
};
@@ -127,11 +125,7 @@ export class CrossLevelMoveCommand extends Command {
* 验证移动的有效性
*/
validateMove() {
const layer = this.findLayer(
this.layerId,
this.fromContainerType,
this.fromParentId
);
const layer = this.findLayer(this.layerId, this.fromContainerType, this.fromParentId);
if (!layer) {
throw new Error(`找不到要移动的图层: ${this.layerId}`);
@@ -153,9 +147,7 @@ export class CrossLevelMoveCommand extends Command {
// 检查目标父图层是否为组
if (this.toContainerType === "child") {
const targetParent = this.layers.value.find(
(layer) => layer.id === this.toParentId
);
const targetParent = this.layers.value.find((layer) => layer.id === this.toParentId);
if (!targetParent || !isGroupLayer(targetParent)) {
throw new Error("目标图层不是组图层");
}
@@ -198,15 +190,9 @@ export class CrossLevelMoveCommand extends Command {
try {
// 根据移动类型执行对应操作
if (
this.fromContainerType === "root" &&
this.toContainerType === "child"
) {
if (this.fromContainerType === "root" && this.toContainerType === "child") {
this.moveRootToGroup(draggedLayer);
} else if (
this.fromContainerType === "child" &&
this.toContainerType === "root"
) {
} else if (this.fromContainerType === "child" && this.toContainerType === "root") {
this.moveGroupToRoot(draggedLayer);
} else if (
this.fromContainerType === "child" &&
@@ -228,6 +214,17 @@ export class CrossLevelMoveCommand extends Command {
await this.layerManager?.updateLayersObjectsInteractivity();
this.canvas?.renderAll();
// 生成缩略图
this.fromParentId &&
this.layerManager?.canvasManager?.thumbnailManager?.generateLayerThumbnail?.(
this.fromParentId
);
this.toParentId &&
this.layerManager?.canvasManager?.thumbnailManager?.generateLayerThumbnail?.(
this.toParentId
);
console.log("✅ 跨层级移动命令执行成功");
return true;
} catch (error) {
@@ -266,7 +263,6 @@ export class CrossLevelMoveCommand extends Command {
this.restoreFabricObjectStates(this.beforeState.fabricObjects);
await this.layerManager?.updateLayersObjectsInteractivity();
this.canvas?.renderAll();
// 刷新画布
this.canvas?.renderAll();
console.log("✅ 跨层级移动命令撤销成功");
@@ -299,9 +295,7 @@ export class CrossLevelMoveCommand extends Command {
// 根据图层的parentId重新放置
if (layer.parentId) {
const parentLayer = this.layers.value.find(
(l) => l.id === layer.parentId
);
const parentLayer = this.layers.value.find((l) => l.id === layer.parentId);
if (
parentLayer &&
parentLayer.fabricObject &&
@@ -396,14 +390,9 @@ export class CrossLevelMoveCommand extends Command {
layer.children = layerData.children
.map((childData) => {
// 从原始图层数组中查找子图层
let childLayer = originalLayers.find(
(l) => l.id === childData.id
);
let childLayer = originalLayers.find((l) => l.id === childData.id);
if (!childLayer) {
childLayer = this.findLayerInOriginalStructure(
childData.id,
originalLayers
);
childLayer = this.findLayerInOriginalStructure(childData.id, originalLayers);
}
if (childLayer) {
// 更新子图层属性
@@ -465,9 +454,7 @@ export class CrossLevelMoveCommand extends Command {
// 根据目标状态放置对象
if (targetState.parentGroupId) {
// 应该在特定组中
const parentLayer = this.layers.value.find(
(l) => l.id === targetState.parentGroupId
);
const parentLayer = this.layers.value.find((l) => l.id === targetState.parentGroupId);
if (
parentLayer &&
parentLayer.fabricObject &&
@@ -532,12 +519,7 @@ export class CrossLevelMoveCommand extends Command {
*/
safeRemoveFromContainer(fabricObject, container, containerName) {
return this.safeFabricOperation(() => {
if (
container &&
container.remove &&
container.contains &&
container.contains(fabricObject)
) {
if (container && container.remove && container.contains && container.contains(fabricObject)) {
container.remove(fabricObject);
console.log(`✅ 成功从${containerName}移除对象`);
return true;
@@ -556,11 +538,7 @@ export class CrossLevelMoveCommand extends Command {
// 从所有组中移除
this.layers.value.forEach((layer) => {
if (layer.fabricObject && layer.fabricObject.type === "group") {
this.safeRemoveFromContainer(
fabricObject,
layer.fabricObject,
`${layer.id}`
);
this.safeRemoveFromContainer(fabricObject, layer.fabricObject, `${layer.id}`);
}
});
}
@@ -575,9 +553,7 @@ export class CrossLevelMoveCommand extends Command {
newIndex: this.newIndex,
});
const targetParent = this.layers.value.find(
(layer) => layer.id === this.toParentId
);
const targetParent = this.layers.value.find((layer) => layer.id === this.toParentId);
if (!targetParent) {
throw new Error(`找不到目标父图层: ${this.toParentId}`);
}
@@ -588,9 +564,7 @@ export class CrossLevelMoveCommand extends Command {
}
// 从顶级图层数组中移除
const rootIndex = this.layers.value.findIndex(
(layer) => layer.id === draggedLayer.id
);
const rootIndex = this.layers.value.findIndex((layer) => layer.id === draggedLayer.id);
if (rootIndex !== -1) {
this.layers.value.splice(rootIndex, 1);
console.log(`🗑️ 从顶级图层数组中移除图层 ${draggedLayer.id}`);
@@ -599,20 +573,14 @@ export class CrossLevelMoveCommand extends Command {
// 更新图层关系
draggedLayer.parentId = this.toParentId;
targetParent.children.splice(this.newIndex, 0, draggedLayer);
console.log(
`📂 将图层 ${draggedLayer.id} 添加到父图层 ${this.toParentId} 的children中`
);
console.log(`📂 将图层 ${draggedLayer.id} 添加到父图层 ${this.toParentId} 的children中`);
// 处理fabric对象的层级关系
if (draggedLayer.fabricObject && targetParent.fabricObject) {
console.log(`🎨 处理fabric对象层级关系`);
// 从画布中移除
this.safeRemoveFromContainer(
draggedLayer.fabricObject,
this.canvas,
"画布"
);
this.safeRemoveFromContainer(draggedLayer.fabricObject, this.canvas, "画布");
// 添加到父组
this.safeAddToContainer(
@@ -635,22 +603,16 @@ export class CrossLevelMoveCommand extends Command {
newIndex: this.newIndex,
});
const sourceParent = this.layers.value.find(
(layer) => layer.id === this.fromParentId
);
const sourceParent = this.layers.value.find((layer) => layer.id === this.fromParentId);
if (!sourceParent || !sourceParent.children) {
throw new Error(`找不到源父图层或其children数组: ${this.fromParentId}`);
}
// 从源父图层的children中移除
const childIndex = sourceParent.children.findIndex(
(child) => child.id === draggedLayer.id
);
const childIndex = sourceParent.children.findIndex((child) => child.id === draggedLayer.id);
if (childIndex !== -1) {
sourceParent.children.splice(childIndex, 1);
console.log(
`🗑️ 从父图层 ${this.fromParentId} 的children中移除图层 ${draggedLayer.id}`
);
console.log(`🗑️ 从父图层 ${this.fromParentId} 的children中移除图层 ${draggedLayer.id}`);
}
// 处理fabric对象的层级关系
@@ -675,9 +637,7 @@ export class CrossLevelMoveCommand extends Command {
// 计算在顶级图层中的插入位置
const targetIndex = Math.min(this.newIndex, this.layers.value.length);
this.layers.value.splice(targetIndex, 0, draggedLayer);
console.log(
`📂 将图层 ${draggedLayer.id} 添加到顶级图层数组位置 ${targetIndex}`
);
console.log(`📂 将图层 ${draggedLayer.id} 添加到顶级图层数组位置 ${targetIndex}`);
console.log("✅ 组内图层移动到顶级完成");
}
@@ -693,26 +653,18 @@ export class CrossLevelMoveCommand extends Command {
newIndex: this.newIndex,
});
const sourceParent = this.layers.value.find(
(layer) => layer.id === this.fromParentId
);
const targetParent = this.layers.value.find(
(layer) => layer.id === this.toParentId
);
const sourceParent = this.layers.value.find((layer) => layer.id === this.fromParentId);
const targetParent = this.layers.value.find((layer) => layer.id === this.toParentId);
if (!sourceParent || !targetParent) {
throw new Error("找不到源父图层或目标父图层");
}
// 从源父图层中移除
const childIndex = sourceParent.children.findIndex(
(child) => child.id === draggedLayer.id
);
const childIndex = sourceParent.children.findIndex((child) => child.id === draggedLayer.id);
if (childIndex !== -1) {
sourceParent.children.splice(childIndex, 1);
console.log(
`🗑️ 从源父图层 ${this.fromParentId} 中移除图层 ${draggedLayer.id}`
);
console.log(`🗑️ 从源父图层 ${this.fromParentId} 中移除图层 ${draggedLayer.id}`);
// 从源父组的fabricObject中移除
if (draggedLayer.fabricObject && sourceParent.fabricObject) {
@@ -726,9 +678,7 @@ export class CrossLevelMoveCommand extends Command {
// 更新图层的parentId
draggedLayer.parentId = this.toParentId;
console.log(
`🔗 更新图层 ${draggedLayer.id} 的parentId为 ${this.toParentId}`
);
console.log(`🔗 更新图层 ${draggedLayer.id} 的parentId为 ${this.toParentId}`);
// 确保目标父图层有children数组
if (!targetParent.children) {

View File

@@ -1,8 +1,4 @@
import {
createLayer,
findInChildLayers,
LayerType,
} from "../utils/layerHelper.js";
import { createLayer, findInChildLayers, LayerType } from "../utils/layerHelper.js";
import { createRasterizedImage } from "../utils/selectionToImage.js";
import { CompositeCommand, Command } from "./Command.js";
import { CreateImageLayerCommand } from "./LayerCommands.js";
@@ -56,9 +52,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
const selectionObject = this.selectionManager.getSelectionObject();
if (selectionObject) {
try {
this._clonedSelectionObject = await this._cloneObject(
selectionObject
);
this._clonedSelectionObject = await this._cloneObject(selectionObject);
console.log("套索抠图:选区对象已克隆保存");
} catch (error) {
console.error("套索抠图:克隆选区对象失败:", error);
@@ -294,10 +288,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
await command.undo();
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
} catch (error) {
console.error(
`❌ 子命令撤销失败: ${command.constructor.name}`,
error
);
console.error(`❌ 子命令撤销失败: ${command.constructor.name}`, error);
// 子命令撤销失败不中断整个撤销过程
}
}
@@ -361,16 +352,11 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
// 递归获取图层及其子图层的所有对象
const collectLayerObjects = (currentLayer) => {
// 处理图层的fabricObjects
if (
currentLayer.fabricObjects &&
Array.isArray(currentLayer.fabricObjects)
) {
if (currentLayer.fabricObjects && Array.isArray(currentLayer.fabricObjects)) {
currentLayer.fabricObjects.forEach((fabricRef) => {
if (fabricRef && fabricRef.id) {
// 从画布中查找真实的对象
const realObject = canvasObjects.find(
(obj) => obj.id === fabricRef.id
);
const realObject = canvasObjects.find((obj) => obj.id === fabricRef.id);
if (realObject && realObject.visible) {
objects.push(realObject);
}
@@ -380,9 +366,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
// 处理单个fabricObject背景图层等
if (currentLayer.fabricObject && currentLayer.fabricObject.id) {
const realObject = canvasObjects.find(
(obj) => obj.id === currentLayer.fabricObject.id
);
const realObject = canvasObjects.find((obj) => obj.id === currentLayer.fabricObject.id);
if (realObject && realObject.visible) {
objects.push(realObject);
}
@@ -413,11 +397,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
* @returns {String} 抠图结果的DataURL
* @private
*/
async _performCutoutWithRasterized(
sourceObjects,
selectionObject,
selectionBounds
) {
async _performCutoutWithRasterized(sourceObjects, selectionObject, selectionBounds) {
try {
console.log("=== 开始使用createRasterizedImage执行抠图 ===");
console.log(`源对象数量: ${sourceObjects.length}`);
@@ -456,10 +436,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
// 如果createRasterizedImage失败回退到原始方法
console.log("⚠️ 回退到原始抠图方法...");
return await this._performCutout(
{ fabricObjects: sourceObjects },
selectionObject
);
return await this._performCutout({ fabricObjects: sourceObjects }, selectionObject);
}
}
@@ -490,9 +467,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
try {
// 收集源图层中的可见对象
const visibleObjects = sourceLayer.fabricObjects.filter(
(obj) => obj.visible
);
const visibleObjects = sourceLayer.fabricObjects.filter((obj) => obj.visible);
if (visibleObjects.length === 0) {
throw new Error("源图层没有可见对象");
@@ -545,10 +520,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
let highResolutionScale = 1;
if (this.highResolutionEnabled) {
const devicePixelRatio = window.devicePixelRatio || 1;
highResolutionScale = Math.max(
this.baseResolutionScale,
devicePixelRatio * 2
);
highResolutionScale = Math.max(this.baseResolutionScale, devicePixelRatio * 2);
}
// 创建用于导出的高分辨率canvas
@@ -559,12 +531,8 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
colorSpace: "srgb",
});
const actualWidth = Math.round(
renderBounds.width * highResolutionScale
);
const actualHeight = Math.round(
renderBounds.height * highResolutionScale
);
const actualWidth = Math.round(renderBounds.width * highResolutionScale);
const actualHeight = Math.round(renderBounds.height * highResolutionScale);
exportCanvas.width = actualWidth;
exportCanvas.height = actualHeight;
@@ -773,17 +741,14 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
}
});
} else if (objectType === "polygon") {
fabric.Polygon.fromObject(
this.serializedSelectionObject,
(polygon) => {
if (polygon) {
console.log("多边形选区对象反序列化成功");
resolve(polygon);
} else {
reject(new Error("多边形选区对象反序列化失败"));
}
fabric.Polygon.fromObject(this.serializedSelectionObject, (polygon) => {
if (polygon) {
console.log("多边形选区对象反序列化成功");
resolve(polygon);
} else {
reject(new Error("多边形选区对象反序列化失败"));
}
);
});
} else if (objectType === "rect") {
fabric.Rect.fromObject(this.serializedSelectionObject, (rect) => {
if (rect) {
@@ -794,30 +759,24 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
}
});
} else if (objectType === "ellipse" || objectType === "circle") {
fabric.Ellipse.fromObject(
this.serializedSelectionObject,
(ellipse) => {
if (ellipse) {
console.log("椭圆选区对象反序列化成功");
resolve(ellipse);
} else {
reject(new Error("椭圆选区对象反序列化失败"));
}
fabric.Ellipse.fromObject(this.serializedSelectionObject, (ellipse) => {
if (ellipse) {
console.log("椭圆选区对象反序列化成功");
resolve(ellipse);
} else {
reject(new Error("椭圆选区对象反序列化失败"));
}
);
});
} else {
// 通用对象反序列化
fabric.util.enlivenObjects(
[this.serializedSelectionObject],
(objects) => {
if (objects && objects.length > 0) {
console.log("通用选区对象反序列化成功");
resolve(objects[0]);
} else {
reject(new Error("通用选区对象反序列化失败"));
}
fabric.util.enlivenObjects([this.serializedSelectionObject], (objects) => {
if (objects && objects.length > 0) {
console.log("通用选区对象反序列化成功");
resolve(objects[0]);
} else {
reject(new Error("通用选区对象反序列化失败"));
}
);
});
}
});
} catch (error) {

View File

@@ -1,8 +1,4 @@
import {
createLayer,
findInChildLayers,
LayerType,
} from "../utils/layerHelper.js";
import { createLayer, findInChildLayers, LayerType } from "../utils/layerHelper.js";
import { createRasterizedImage } from "../utils/selectionToImage.js";
import { CompositeCommand, Command } from "./Command.js";
import { CreateImageLayerCommand } from "./LayerCommands.js";
@@ -59,9 +55,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
const selectionObject = this.selectionManager.getSelectionObject();
if (selectionObject) {
try {
this._clonedSelectionObject = await this._cloneObject(
selectionObject
);
this._clonedSelectionObject = await this._cloneObject(selectionObject);
console.log("剪切选区:选区对象已克隆保存");
} catch (error) {
console.error("剪切选区:克隆选区对象失败:", error);
@@ -132,11 +126,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
}
// 步骤2: 对原图层进行栅格化处理,移除选区内容
await this._rasterizeOriginalLayerWithCutout(
sourceLayer,
sourceObjects,
selectionObject
);
await this._rasterizeOriginalLayerWithCutout(sourceLayer, sourceObjects, selectionObject);
// 步骤3: 创建图像图层命令
const createImageLayerCmd = new CreateImageLayerCommand({
@@ -246,10 +236,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
}
} catch (error) {
console.error(
`❌ 子命令撤销失败: ${command.constructor.name}`,
error
);
console.error(`❌ 子命令撤销失败: ${command.constructor.name}`, error);
// 子命令撤销失败不中断整个撤销过程
}
}
@@ -313,17 +300,14 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
await this._restoreObjectsFromBackup(sourceLayer);
} else {
// 备用方案恢复原始的fabricObjects数组
sourceLayer.fabricObjects =
this.originalLayerBackup.fabricObjects || [];
sourceLayer.fabricObjects = this.originalLayerBackup.fabricObjects || [];
if (sourceLayer.fabricObjects.length > 0) {
await this._restoreLayerObjects(sourceLayer);
}
}
console.log(
`✅ 原图层状态恢复完成,恢复了 ${sourceLayer.fabricObjects.length} 个对象`
);
console.log(`✅ 原图层状态恢复完成,恢复了 ${sourceLayer.fabricObjects.length} 个对象`);
} catch (error) {
console.error("恢复原图层状态失败:", error);
throw error;
@@ -337,78 +321,68 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
*/
async _restoreObjectsFromBackup(layer) {
return new Promise((resolve, reject) => {
if (
!this.originalLayerObjectsData ||
this.originalLayerObjectsData.length === 0
) {
if (!this.originalLayerObjectsData || this.originalLayerObjectsData.length === 0) {
console.warn("没有对象备份数据");
resolve();
return;
}
try {
console.log(
`开始从备份恢复 ${this.originalLayerObjectsData.length} 个对象...`
);
console.log(`开始从备份恢复 ${this.originalLayerObjectsData.length} 个对象...`);
// 使用fabric.util.enlivenObjects重建对象
fabric.util.enlivenObjects(
this.originalLayerObjectsData,
(restoredObjects) => {
try {
let successCount = 0;
fabric.util.enlivenObjects(this.originalLayerObjectsData, (restoredObjects) => {
try {
let successCount = 0;
restoredObjects.forEach((obj, index) => {
if (obj) {
// 确保对象有正确的属性
obj.set({
layerId: layer.id,
layerName: layer.name,
selectable: true,
evented: true,
visible: true,
});
restoredObjects.forEach((obj, index) => {
if (obj) {
// 确保对象有正确的属性
obj.set({
layerId: layer.id,
layerName: layer.name,
selectable: true,
evented: true,
visible: true,
});
// 添加到画布
this.canvas.add(obj);
// 添加到画布
this.canvas.add(obj);
// 添加到图层的fabricObjects数组
layer.fabricObjects.push(
obj.toObject([
"id",
"layerId",
"layerName",
"parentId",
"selectable",
"evented",
"visible",
])
);
// 添加到图层的fabricObjects数组
layer.fabricObjects.push(
obj.toObject([
"id",
"layerId",
"layerName",
"parentId",
"selectable",
"evented",
"visible",
])
);
successCount++;
console.log(
`恢复对象 ${index + 1}/${restoredObjects.length}: ${
obj.id || obj.type
}`
);
} else {
console.warn(`对象 ${index + 1} 恢复失败`);
}
});
successCount++;
console.log(
`恢复对象 ${index + 1}/${restoredObjects.length}: ${obj.id || obj.type}`
);
} else {
console.warn(`对象 ${index + 1} 恢复失败`);
}
});
// 重新渲染画布
this.canvas.renderAll();
// 重新渲染画布
this.canvas.renderAll();
console.log(
`✅ 成功恢复了 ${successCount}/${this.originalLayerObjectsData.length} 个对象`
);
resolve();
} catch (error) {
console.error("处理恢复的对象时出错:", error);
reject(error);
}
console.log(
`✅ 成功恢复了 ${successCount}/${this.originalLayerObjectsData.length} 个对象`
);
resolve();
} catch (error) {
console.error("处理恢复的对象时出错:", error);
reject(error);
}
);
});
} catch (error) {
console.error("恢复对象时出错:", error);
reject(error);
@@ -474,11 +448,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
* @returns {fabric.Image} 抠图结果的fabric图像对象
* @private
*/
async _performCutoutWithRasterized(
sourceObjects,
selectionObject,
selectionBounds
) {
async _performCutoutWithRasterized(sourceObjects, selectionObject, selectionBounds) {
try {
console.log("=== 开始使用createRasterizedImage执行剪切抠图 ===");
console.log(`源对象数量: ${sourceObjects.length}`);
@@ -488,10 +458,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
let scaleFactor = this.baseResolutionScale;
if (this.highResolutionEnabled) {
const currentZoom = this.canvas.getZoom?.() || 1;
scaleFactor = Math.max(
scaleFactor || this.canvas?.getRetinaScaling?.(),
currentZoom
);
scaleFactor = Math.max(scaleFactor || this.canvas?.getRetinaScaling?.(), currentZoom);
scaleFactor = Math.min(scaleFactor, 3);
}
@@ -535,15 +502,10 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
// 递归获取图层及其子图层的所有对象
const collectLayerObjects = (currentLayer) => {
// 处理图层的fabricObjects
if (
currentLayer.fabricObjects &&
Array.isArray(currentLayer.fabricObjects)
) {
if (currentLayer.fabricObjects && Array.isArray(currentLayer.fabricObjects)) {
currentLayer.fabricObjects.forEach((fabricObj) => {
if (fabricObj && fabricObj.id) {
const realObject = canvasObjects.find(
(canvasObj) => canvasObj.id === fabricObj.id
);
const realObject = canvasObjects.find((canvasObj) => canvasObj.id === fabricObj.id);
if (realObject && realObject.visible !== false) {
objects.push(realObject);
}
@@ -649,17 +611,14 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
}
});
} else if (objectType === "polygon") {
fabric.Polygon.fromObject(
this.serializedSelectionObject,
(polygon) => {
if (polygon) {
console.log("多边形选区对象反序列化成功");
resolve(polygon);
} else {
reject(new Error("多边形选区对象反序列化失败"));
}
fabric.Polygon.fromObject(this.serializedSelectionObject, (polygon) => {
if (polygon) {
console.log("多边形选区对象反序列化成功");
resolve(polygon);
} else {
reject(new Error("多边形选区对象反序列化失败"));
}
);
});
} else if (objectType === "rect") {
fabric.Rect.fromObject(this.serializedSelectionObject, (rect) => {
if (rect) {
@@ -670,30 +629,24 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
}
});
} else if (objectType === "ellipse" || objectType === "circle") {
fabric.Ellipse.fromObject(
this.serializedSelectionObject,
(ellipse) => {
if (ellipse) {
console.log("椭圆选区对象反序列化成功");
resolve(ellipse);
} else {
reject(new Error("椭圆选区对象反序列化失败"));
}
fabric.Ellipse.fromObject(this.serializedSelectionObject, (ellipse) => {
if (ellipse) {
console.log("椭圆选区对象反序列化成功");
resolve(ellipse);
} else {
reject(new Error("椭圆选区对象反序列化失败"));
}
);
});
} else {
// 通用对象反序列化
fabric.util.enlivenObjects(
[this.serializedSelectionObject],
(objects) => {
if (objects && objects.length > 0) {
console.log("通用选区对象反序列化成功");
resolve(objects[0]);
} else {
reject(new Error("通用选区对象反序列化失败"));
}
fabric.util.enlivenObjects([this.serializedSelectionObject], (objects) => {
if (objects && objects.length > 0) {
console.log("通用选区对象反序列化成功");
resolve(objects[0]);
} else {
reject(new Error("通用选区对象反序列化失败"));
}
);
});
}
});
} catch (error) {
@@ -761,9 +714,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
this.originalLayerRasterized = false; // 标记原图层是否已栅格化
console.log(
`✅ 原图层状态已备份,对象数量: ${this.originalLayerObjectsData.length}`
);
console.log(`✅ 原图层状态已备份,对象数量: ${this.originalLayerObjectsData.length}`);
} catch (error) {
console.error("备份原图层状态失败:", error);
throw error;
@@ -777,11 +728,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
* @param {Object} selectionObject 选区对象
* @private
*/
async _rasterizeOriginalLayerWithCutout(
sourceLayer,
sourceObjects,
selectionObject
) {
async _rasterizeOriginalLayerWithCutout(sourceLayer, sourceObjects, selectionObject) {
try {
console.log("=== 开始对原图层进行栅格化处理,移除选区内容 ===");

View File

@@ -92,9 +92,6 @@ export class EraserCommand extends Command {
if (!this.layerManager) return;
const canvasObjects = this.canvas.getObjects();
restoreObjectLayerAssociations(
this.layerManager?.layers?.value,
canvasObjects
);
restoreObjectLayerAssociations(this.layerManager?.layers?.value, canvasObjects);
}
}

View File

@@ -1,10 +1,5 @@
import { generateId } from "../utils/helper";
import {
createLayer,
findLayerRecursively,
LayerType,
OperationType,
} from "../utils/layerHelper";
import { createLayer, findLayerRecursively, LayerType, OperationType } from "../utils/layerHelper";
import { Command } from "./Command";
import {
findObjectById,
@@ -33,14 +28,12 @@ export class MergeGroupLayerCommand extends Command {
this.originalObjects = [...this.canvas.getObjects()];
this.flattenedLayer = null;
this.flattenedLayerId =
generateId("flattened_") ||
`flattened_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
generateId("flattened_") || `flattened_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
this.existingGroupId = null; // 用于查找现有组对象
// 组对象相关
this.newGroupId =
generateId("group") ||
`group_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
generateId("group") || `group_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
}
async execute() {
@@ -172,15 +165,11 @@ export class MergeGroupLayerCommand extends Command {
});
// 获取源图层在数组中的索引
const sourceIndex = this.layers.value.findIndex(
(l) => l.id === this.layerId
);
const sourceIndex = this.layers.value.findIndex((l) => l.id === this.layerId);
// 移除所有相关图层
const layerIdsToRemove = layersToFlatten.map((layer) => layer.id);
this.layers.value = this.layers.value.filter(
(layer) => !layerIdsToRemove.includes(layer.id)
);
this.layers.value = this.layers.value.filter((layer) => !layerIdsToRemove.includes(layer.id));
// 在原位置插入展平后的图层
this.layers.value.splice(sourceIndex, 0, this.flattenedLayer);
@@ -202,11 +191,7 @@ export class MergeGroupLayerCommand extends Command {
}
async undo() {
if (
!this.flattenedLayer ||
!this.originalLayers ||
!this.originalObjectStates
) {
if (!this.flattenedLayer || !this.originalLayers || !this.originalObjectStates) {
console.warn("没有可撤销的数据");
return;
}
@@ -324,10 +309,7 @@ export class MergeGroupLayerCommand extends Command {
groupObjects.forEach((obj) => {
try {
// 计算对象的绝对位置
const absolutePosition = this._calculateObjectAbsolutePosition(
obj,
groupObject
);
const absolutePosition = this._calculateObjectAbsolutePosition(obj, groupObject);
// 使用removeWithUpdate移除对象这会自动恢复对象的独立状态
groupObject.removeWithUpdate(obj);
@@ -382,10 +364,7 @@ export class MergeGroupLayerCommand extends Command {
const objectPoint = new fabric.Point(obj.left || 0, obj.top || 0);
// 应用组的变换矩阵计算绝对位置
const absolutePoint = fabric.util.transformPoint(
objectPoint,
groupTransform
);
const absolutePoint = fabric.util.transformPoint(objectPoint, groupTransform);
// 计算缩放比例
const totalScaleX = (group.scaleX || 1) * (obj.scaleX || 1);
@@ -483,9 +462,7 @@ export class MergeGroupLayerCommand extends Command {
canvasObj.layerId = layer.id;
canvasObj.layerName = layer.name;
console.log(
`🔗 恢复对象 ${canvasObj.id} 与图层 ${layer.name} 的关联`
);
console.log(`🔗 恢复对象 ${canvasObj.id} 与图层 ${layer.name} 的关联`);
}
}
});

View File

@@ -1,14 +1,7 @@
import {
createLayer,
findInChildLayers,
LayerType,
} from "../utils/layerHelper.js";
import { createLayer, findInChildLayers, LayerType } from "../utils/layerHelper.js";
import { createRasterizedImage } from "../utils/selectionToImage.js";
import { CompositeCommand, Command } from "./Command.js";
import {
CreateImageLayerCommand,
RemoveLayerCommand,
} from "./LayerCommands.js";
import { CreateImageLayerCommand, RemoveLayerCommand } from "./LayerCommands.js";
import { fabric } from "fabric-with-all";
import { generateId } from "../utils/helper.js";
@@ -65,9 +58,7 @@ export class LassoCutoutCommand extends CompositeCommand {
const selectionObject = this.selectionManager.getSelectionObject();
if (selectionObject) {
try {
this._clonedSelectionObject = await this._cloneObject(
selectionObject
);
this._clonedSelectionObject = await this._cloneObject(selectionObject);
console.log("套索抠图:选区对象已克隆保存");
} catch (error) {
console.error("套索抠图:克隆选区对象失败:", error);
@@ -103,14 +94,7 @@ export class LassoCutoutCommand extends CompositeCommand {
const sourceObjects = this._getLayerObjects(activeLayer);
this.originalCanvasObjects = sourceObjects; // 保存真实对象引用
this.originalFabricObjects = sourceObjects.map((obj) =>
obj.toObject([
"id",
"layerId",
"layerName",
"parentId",
"type",
"custom",
])
obj.toObject(["id", "layerId", "layerName", "parentId", "type", "custom"])
);
console.log(
@@ -353,10 +337,7 @@ export class LassoCutoutCommand extends CompositeCommand {
await command.undo();
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
} catch (error) {
console.error(
`❌ 子命令撤销失败: ${command.constructor.name}`,
error
);
console.error(`❌ 子命令撤销失败: ${command.constructor.name}`, error);
// 子命令撤销失败不中断整个撤销过程
}
}
@@ -433,16 +414,11 @@ export class LassoCutoutCommand extends CompositeCommand {
// 递归获取图层及其子图层的所有对象
const collectLayerObjects = (currentLayer) => {
// 处理图层的fabricObjects
if (
currentLayer.fabricObjects &&
Array.isArray(currentLayer.fabricObjects)
) {
if (currentLayer.fabricObjects && Array.isArray(currentLayer.fabricObjects)) {
currentLayer.fabricObjects.forEach((fabricRef) => {
if (fabricRef && fabricRef.id) {
// 从画布中查找真实的对象
const realObject = canvasObjects.find(
(obj) => obj.id === fabricRef.id
);
const realObject = canvasObjects.find((obj) => obj.id === fabricRef.id);
if (realObject && realObject.visible) {
objects.push(realObject);
}
@@ -452,9 +428,7 @@ export class LassoCutoutCommand extends CompositeCommand {
// 处理单个fabricObject背景图层等
if (currentLayer.fabricObject && currentLayer.fabricObject.id) {
const realObject = canvasObjects.find(
(obj) => obj.id === currentLayer.fabricObject.id
);
const realObject = canvasObjects.find((obj) => obj.id === currentLayer.fabricObject.id);
if (realObject && realObject.visible) {
objects.push(realObject);
}
@@ -485,11 +459,7 @@ export class LassoCutoutCommand extends CompositeCommand {
* @returns {String} 抠图结果的DataURL
* @private
*/
async _performCutoutWithRasterized(
sourceObjects,
selectionObject,
selectionBounds
) {
async _performCutoutWithRasterized(sourceObjects, selectionObject, selectionBounds) {
try {
console.log("=== 开始使用createRasterizedImage执行抠图 ===");
console.log(`源对象数量: ${sourceObjects.length}`);
@@ -528,10 +498,7 @@ export class LassoCutoutCommand extends CompositeCommand {
// 如果createRasterizedImage失败回退到原始方法
console.log("⚠️ 回退到原始抠图方法...");
return await this._performCutout(
{ fabricObjects: sourceObjects },
selectionObject
);
return await this._performCutout({ fabricObjects: sourceObjects }, selectionObject);
}
}
@@ -562,9 +529,7 @@ export class LassoCutoutCommand extends CompositeCommand {
try {
// 收集源图层中的可见对象
const visibleObjects = sourceLayer.fabricObjects.filter(
(obj) => obj.visible
);
const visibleObjects = sourceLayer.fabricObjects.filter((obj) => obj.visible);
if (visibleObjects.length === 0) {
throw new Error("源图层没有可见对象");
@@ -617,10 +582,7 @@ export class LassoCutoutCommand extends CompositeCommand {
let highResolutionScale = 1;
if (this.highResolutionEnabled) {
const devicePixelRatio = window.devicePixelRatio || 1;
highResolutionScale = Math.max(
this.baseResolutionScale,
devicePixelRatio * 2
);
highResolutionScale = Math.max(this.baseResolutionScale, devicePixelRatio * 2);
}
// 创建用于导出的高分辨率canvas
@@ -631,12 +593,8 @@ export class LassoCutoutCommand extends CompositeCommand {
colorSpace: "srgb",
});
const actualWidth = Math.round(
renderBounds.width * highResolutionScale
);
const actualHeight = Math.round(
renderBounds.height * highResolutionScale
);
const actualWidth = Math.round(renderBounds.width * highResolutionScale);
const actualHeight = Math.round(renderBounds.height * highResolutionScale);
exportCanvas.width = actualWidth;
exportCanvas.height = actualHeight;
@@ -845,17 +803,14 @@ export class LassoCutoutCommand extends CompositeCommand {
}
});
} else if (objectType === "polygon") {
fabric.Polygon.fromObject(
this.serializedSelectionObject,
(polygon) => {
if (polygon) {
console.log("多边形选区对象反序列化成功");
resolve(polygon);
} else {
reject(new Error("多边形选区对象反序列化失败"));
}
fabric.Polygon.fromObject(this.serializedSelectionObject, (polygon) => {
if (polygon) {
console.log("多边形选区对象反序列化成功");
resolve(polygon);
} else {
reject(new Error("多边形选区对象反序列化失败"));
}
);
});
} else if (objectType === "rect") {
fabric.Rect.fromObject(this.serializedSelectionObject, (rect) => {
if (rect) {
@@ -866,30 +821,24 @@ export class LassoCutoutCommand extends CompositeCommand {
}
});
} else if (objectType === "ellipse" || objectType === "circle") {
fabric.Ellipse.fromObject(
this.serializedSelectionObject,
(ellipse) => {
if (ellipse) {
console.log("椭圆选区对象反序列化成功");
resolve(ellipse);
} else {
reject(new Error("椭圆选区对象反序列化失败"));
}
fabric.Ellipse.fromObject(this.serializedSelectionObject, (ellipse) => {
if (ellipse) {
console.log("椭圆选区对象反序列化成功");
resolve(ellipse);
} else {
reject(new Error("椭圆选区对象反序列化失败"));
}
);
});
} else {
// 通用对象反序列化
fabric.util.enlivenObjects(
[this.serializedSelectionObject],
(objects) => {
if (objects && objects.length > 0) {
console.log("通用选区对象反序列化成功");
resolve(objects[0]);
} else {
reject(new Error("通用选区对象反序列化失败"));
}
fabric.util.enlivenObjects([this.serializedSelectionObject], (objects) => {
if (objects && objects.length > 0) {
console.log("通用选区对象反序列化成功");
resolve(objects[0]);
} else {
reject(new Error("通用选区对象反序列化失败"));
}
);
});
}
});
} catch (error) {
@@ -913,11 +862,7 @@ export class LassoCutoutCommand extends CompositeCommand {
// 1. 恢复图层到原位置
if (this.originalLayerIndex !== -1) {
this.layerManager.layers.value.splice(
this.originalLayerIndex,
0,
this.originalLayer
);
this.layerManager.layers.value.splice(this.originalLayerIndex, 0, this.originalLayer);
} else {
// 如果没有保存索引,添加到末尾
this.layerManager.layers.value.push(this.originalLayer);
@@ -925,9 +870,7 @@ export class LassoCutoutCommand extends CompositeCommand {
// 2. 恢复fabric对象到画布
if (this.originalFabricObjects.length > 0) {
console.log(
`↩️ 恢复 ${this.originalFabricObjects.length} 个fabric对象`
);
console.log(`↩️ 恢复 ${this.originalFabricObjects.length} 个fabric对象`);
// 使用fabric.util.enlivenObjects批量反序列化对象
await new Promise((resolve, reject) => {
@@ -1076,12 +1019,7 @@ export class ClearSelectionCommand extends Command {
// 序列化选区对象和相关状态
this.originalSelectionState = {
// 选区对象的序列化数据
selectionObjectData: selectionObject.toObject([
"id",
"layerId",
"layerName",
"parentId",
]),
selectionObjectData: selectionObject.toObject(["id", "layerId", "layerName", "parentId"]),
// 选区路径数据
selectionPath: this.selectionManager.getSelectionPath(),
// 羽化值
@@ -1144,13 +1082,8 @@ export class ClearSelectionCommand extends Command {
return;
}
const {
selectionObjectData,
featherAmount,
selectionStyle,
position,
managerState,
} = this.originalSelectionState;
const { selectionObjectData, featherAmount, selectionStyle, position, managerState } =
this.originalSelectionState;
// 根据选区对象类型进行反序列化
const objectType = selectionObjectData.type;
@@ -1196,9 +1129,7 @@ export class ClearSelectionCommand extends Command {
// 恢复阴影(羽化效果)
if (selectionStyle.shadow) {
restoredObject.shadow = new fabric.Shadow(
selectionStyle.shadow
);
restoredObject.shadow = new fabric.Shadow(selectionStyle.shadow);
}
}
@@ -1283,9 +1214,7 @@ export class ClearSelectionCommand extends Command {
// 验证基本属性
const originalId = this.originalSelectionState.selectionId;
if (currentSelection.id !== originalId) {
console.warn(
`选区ID不匹配: 期望 ${originalId}, 实际 ${currentSelection.id}`
);
console.warn(`选区ID不匹配: 期望 ${originalId}, 实际 ${currentSelection.id}`);
return false;
}
@@ -1293,9 +1222,7 @@ export class ClearSelectionCommand extends Command {
const currentFeather = this.selectionManager.getFeatherAmount();
const originalFeather = this.originalSelectionState.featherAmount;
if (currentFeather !== originalFeather) {
console.warn(
`羽化值不匹配: 期望 ${originalFeather}, 实际 ${currentFeather}`
);
console.warn(`羽化值不匹配: 期望 ${originalFeather}, 实际 ${currentFeather}`);
return false;
}

View File

@@ -1,15 +1,7 @@
import {
createLayer,
findInChildLayers,
LayerType,
OperationType,
} from "../utils/layerHelper.js";
import { createLayer, findInChildLayers, LayerType, OperationType } from "../utils/layerHelper.js";
import { createRasterizedImage } from "../utils/selectionToImage.js";
import { CompositeCommand, Command } from "./Command.js";
import {
CreateImageLayerCommand,
RemoveLayerCommand,
} from "./LayerCommands.js";
import { CreateImageLayerCommand, RemoveLayerCommand } from "./LayerCommands.js";
import { fabric } from "fabric-with-all";
import { generateId } from "../utils/helper.js";
import { ToolCommand } from "./ToolCommands.js";
@@ -69,9 +61,7 @@ export class LassoCutoutCommand extends CompositeCommand {
const selectionObject = this.selectionManager.getSelectionObject();
if (selectionObject) {
try {
this._clonedSelectionObject = await this._cloneObject(
selectionObject
);
this._clonedSelectionObject = await this._cloneObject(selectionObject);
console.log("套索抠图:选区对象已克隆保存");
} catch (error) {
console.error("套索抠图:克隆选区对象失败:", error);
@@ -107,14 +97,7 @@ export class LassoCutoutCommand extends CompositeCommand {
const sourceObjects = this._getLayerObjects(activeLayer);
this.originalCanvasObjects = sourceObjects; // 保存真实对象引用
this.originalFabricObjects = sourceObjects.map((obj) =>
obj.toObject([
"id",
"layerId",
"layerName",
"parentId",
"type",
"custom",
])
obj.toObject(["id", "layerId", "layerName", "parentId", "type", "custom"])
);
console.log(
@@ -385,10 +368,7 @@ export class LassoCutoutCommand extends CompositeCommand {
await command.undo();
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
} catch (error) {
console.error(
`❌ 子命令撤销失败: ${command.constructor.name}`,
error
);
console.error(`❌ 子命令撤销失败: ${command.constructor.name}`, error);
// 子命令撤销失败不中断整个撤销过程
}
}
@@ -465,16 +445,11 @@ export class LassoCutoutCommand extends CompositeCommand {
// 递归获取图层及其子图层的所有对象
const collectLayerObjects = (currentLayer) => {
// 处理图层的fabricObjects
if (
currentLayer.fabricObjects &&
Array.isArray(currentLayer.fabricObjects)
) {
if (currentLayer.fabricObjects && Array.isArray(currentLayer.fabricObjects)) {
currentLayer.fabricObjects.forEach((fabricRef) => {
if (fabricRef && fabricRef.id) {
// 从画布中查找真实的对象
const realObject = canvasObjects.find(
(obj) => obj.id === fabricRef.id
);
const realObject = canvasObjects.find((obj) => obj.id === fabricRef.id);
if (realObject && realObject.visible) {
objects.push(realObject);
}
@@ -484,9 +459,7 @@ export class LassoCutoutCommand extends CompositeCommand {
// 处理单个fabricObject背景图层等
if (currentLayer.fabricObject && currentLayer.fabricObject.id) {
const realObject = canvasObjects.find(
(obj) => obj.id === currentLayer.fabricObject.id
);
const realObject = canvasObjects.find((obj) => obj.id === currentLayer.fabricObject.id);
if (realObject && realObject.visible) {
objects.push(realObject);
}
@@ -517,11 +490,7 @@ export class LassoCutoutCommand extends CompositeCommand {
* @returns {String} 抠图结果的DataURL
* @private
*/
async _performCutoutWithRasterized(
sourceObjects,
selectionObject,
selectionBounds
) {
async _performCutoutWithRasterized(sourceObjects, selectionObject, selectionBounds) {
try {
console.log("=== 开始使用createRasterizedImage执行抠图 ===");
console.log(`源对象数量: ${sourceObjects.length}`);
@@ -560,10 +529,7 @@ export class LassoCutoutCommand extends CompositeCommand {
// 如果createRasterizedImage失败回退到原始方法
console.log("⚠️ 回退到原始抠图方法...");
return await this._performCutout(
{ fabricObjects: sourceObjects },
selectionObject
);
return await this._performCutout({ fabricObjects: sourceObjects }, selectionObject);
}
}
@@ -594,9 +560,7 @@ export class LassoCutoutCommand extends CompositeCommand {
try {
// 收集源图层中的可见对象
const visibleObjects = sourceLayer.fabricObjects.filter(
(obj) => obj.visible
);
const visibleObjects = sourceLayer.fabricObjects.filter((obj) => obj.visible);
if (visibleObjects.length === 0) {
throw new Error("源图层没有可见对象");
@@ -649,10 +613,7 @@ export class LassoCutoutCommand extends CompositeCommand {
let highResolutionScale = 1;
if (this.highResolutionEnabled) {
const devicePixelRatio = window.devicePixelRatio || 1;
highResolutionScale = Math.max(
this.baseResolutionScale,
devicePixelRatio * 2
);
highResolutionScale = Math.max(this.baseResolutionScale, devicePixelRatio * 2);
}
// 创建用于导出的高分辨率canvas
@@ -663,12 +624,8 @@ export class LassoCutoutCommand extends CompositeCommand {
colorSpace: "srgb",
});
const actualWidth = Math.round(
renderBounds.width * highResolutionScale
);
const actualHeight = Math.round(
renderBounds.height * highResolutionScale
);
const actualWidth = Math.round(renderBounds.width * highResolutionScale);
const actualHeight = Math.round(renderBounds.height * highResolutionScale);
exportCanvas.width = actualWidth;
exportCanvas.height = actualHeight;
@@ -877,17 +834,14 @@ export class LassoCutoutCommand extends CompositeCommand {
}
});
} else if (objectType === "polygon") {
fabric.Polygon.fromObject(
this.serializedSelectionObject,
(polygon) => {
if (polygon) {
console.log("多边形选区对象反序列化成功");
resolve(polygon);
} else {
reject(new Error("多边形选区对象反序列化失败"));
}
fabric.Polygon.fromObject(this.serializedSelectionObject, (polygon) => {
if (polygon) {
console.log("多边形选区对象反序列化成功");
resolve(polygon);
} else {
reject(new Error("多边形选区对象反序列化失败"));
}
);
});
} else if (objectType === "rect") {
fabric.Rect.fromObject(this.serializedSelectionObject, (rect) => {
if (rect) {
@@ -898,30 +852,24 @@ export class LassoCutoutCommand extends CompositeCommand {
}
});
} else if (objectType === "ellipse" || objectType === "circle") {
fabric.Ellipse.fromObject(
this.serializedSelectionObject,
(ellipse) => {
if (ellipse) {
console.log("椭圆选区对象反序列化成功");
resolve(ellipse);
} else {
reject(new Error("椭圆选区对象反序列化失败"));
}
fabric.Ellipse.fromObject(this.serializedSelectionObject, (ellipse) => {
if (ellipse) {
console.log("椭圆选区对象反序列化成功");
resolve(ellipse);
} else {
reject(new Error("椭圆选区对象反序列化失败"));
}
);
});
} else {
// 通用对象反序列化
fabric.util.enlivenObjects(
[this.serializedSelectionObject],
(objects) => {
if (objects && objects.length > 0) {
console.log("通用选区对象反序列化成功");
resolve(objects[0]);
} else {
reject(new Error("通用选区对象反序列化失败"));
}
fabric.util.enlivenObjects([this.serializedSelectionObject], (objects) => {
if (objects && objects.length > 0) {
console.log("通用选区对象反序列化成功");
resolve(objects[0]);
} else {
reject(new Error("通用选区对象反序列化失败"));
}
);
});
}
});
} catch (error) {
@@ -945,11 +893,7 @@ export class LassoCutoutCommand extends CompositeCommand {
// 1. 恢复图层到原位置
if (this.originalLayerIndex !== -1) {
this.layerManager.layers.value.splice(
this.originalLayerIndex,
0,
this.originalLayer
);
this.layerManager.layers.value.splice(this.originalLayerIndex, 0, this.originalLayer);
} else {
// 如果没有保存索引,添加到末尾
this.layerManager.layers.value.push(this.originalLayer);
@@ -957,9 +901,7 @@ export class LassoCutoutCommand extends CompositeCommand {
// 2. 恢复fabric对象到画布
if (this.originalFabricObjects.length > 0) {
console.log(
`↩️ 恢复 ${this.originalFabricObjects.length} 个fabric对象`
);
console.log(`↩️ 恢复 ${this.originalFabricObjects.length} 个fabric对象`);
// 使用fabric.util.enlivenObjects批量反序列化对象
await new Promise((resolve, reject) => {
@@ -1108,12 +1050,7 @@ export class ClearSelectionCommand extends Command {
// 序列化选区对象和相关状态
this.originalSelectionState = {
// 选区对象的序列化数据
selectionObjectData: selectionObject.toObject([
"id",
"layerId",
"layerName",
"parentId",
]),
selectionObjectData: selectionObject.toObject(["id", "layerId", "layerName", "parentId"]),
// 选区路径数据
selectionPath: this.selectionManager.getSelectionPath(),
// 羽化值
@@ -1176,13 +1113,8 @@ export class ClearSelectionCommand extends Command {
return;
}
const {
selectionObjectData,
featherAmount,
selectionStyle,
position,
managerState,
} = this.originalSelectionState;
const { selectionObjectData, featherAmount, selectionStyle, position, managerState } =
this.originalSelectionState;
// 根据选区对象类型进行反序列化
const objectType = selectionObjectData.type;
@@ -1228,9 +1160,7 @@ export class ClearSelectionCommand extends Command {
// 恢复阴影(羽化效果)
if (selectionStyle.shadow) {
restoredObject.shadow = new fabric.Shadow(
selectionStyle.shadow
);
restoredObject.shadow = new fabric.Shadow(selectionStyle.shadow);
}
}
@@ -1315,9 +1245,7 @@ export class ClearSelectionCommand extends Command {
// 验证基本属性
const originalId = this.originalSelectionState.selectionId;
if (currentSelection.id !== originalId) {
console.warn(
`选区ID不匹配: 期望 ${originalId}, 实际 ${currentSelection.id}`
);
console.warn(`选区ID不匹配: 期望 ${originalId}, 实际 ${currentSelection.id}`);
return false;
}
@@ -1325,9 +1253,7 @@ export class ClearSelectionCommand extends Command {
const currentFeather = this.selectionManager.getFeatherAmount();
const originalFeather = this.originalSelectionState.featherAmount;
if (currentFeather !== originalFeather) {
console.warn(
`羽化值不匹配: 期望 ${originalFeather}, 实际 ${currentFeather}`
);
console.warn(`羽化值不匹配: 期望 ${originalFeather}, 实际 ${currentFeather}`);
return false;
}

View File

@@ -1,16 +1,13 @@
import { Command } from "./Command";
import {
createLayer,
findLayerRecursively,
LayerType,
OperationType,
} from "../utils/layerHelper";
import { createLayer, findLayerRecursively, LayerType, OperationType } from "../utils/layerHelper";
import { createStaticCanvas } from "../utils/canvasFactory";
import { AddObjectToLayerCommand } from "./ObjectLayerCommands";
import { ToolCommand } from "./ToolCommands";
import {
findObjectById,
generateId,
getObjectZIndex,
insertObjectAtZIndex,
objectIsInCanvas,
optimizeCanvasRendering,
removeCanvasObjectByObject,
@@ -112,15 +109,11 @@ export class AddLayerCommand extends Command {
parentLayer.children = parentLayer.children || [];
parentLayer.children.splice(insertIndex, 0, newLayer);
console.log(
`新图层已插入到子图层位置: ${insertIndex} (父图层: ${parentLayer.name})`
);
console.log(`新图层已插入到子图层位置: ${insertIndex} (父图层: ${parentLayer.name})`);
} else {
// 当前激活图层是一级图层
// 在一级图层中,插入到激活图层之上
const activeLayerIndex = layers.findIndex(
(layer) => layer.id === currentActiveLayerId
);
const activeLayerIndex = layers.findIndex((layer) => layer.id === currentActiveLayerId);
insertIndex = Math.max(0, activeLayerIndex);
layers.splice(insertIndex, 0, newLayer);
@@ -273,9 +266,7 @@ export class PasteLayerCommand extends Command {
try {
objects.forEach((obj) => {
// 生成新的对象ID
const newObjId = `obj_${Date.now()}_${Math.floor(
Math.random() * 1000
)}`;
const newObjId = `obj_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
obj.id = newObjId;
obj.layerId = this.newLayerId;
obj.layerName = this.newLayer.name;
@@ -337,9 +328,7 @@ export class PasteLayerCommand extends Command {
if (!this.newLayer || !this.newLayerId) return;
// 从图层列表删除该图层
const index = this.layers.value.findIndex(
(layer) => layer.id === this.newLayerId
);
const index = this.layers.value.findIndex((layer) => layer.id === this.newLayerId);
if (index !== -1) {
this.layers.value.splice(index, 1);
}
@@ -405,9 +394,7 @@ export class RemoveLayerCommand extends Command {
this.activeLayerId = options.activeLayerId;
// 查找要删除的图层
this.layerIndex = this.layers.value.findIndex(
(layer) => layer.id === this.layerId
);
this.layerIndex = this.layers.value.findIndex((layer) => layer.id === this.layerId);
this.removedLayer = this.layers.value[this.layerIndex];
this.isActiveLayer = this.layerId === this.activeLayerId.value;
@@ -474,9 +461,7 @@ export class RemoveLayerCommand extends Command {
// 如果删除的是当前活动图层,需要更新活动图层
if (this.isActiveLayer) {
// 查找最近的非背景层作为新的活动图层
const newActiveLayer = this.layers.value.find(
(layer) => !layer.isBackground
);
const newActiveLayer = this.layers.value.find((layer) => !layer.isBackground);
if (newActiveLayer) {
this.activeLayerId.value = newActiveLayer.id;
} else {
@@ -554,30 +539,22 @@ export class MoveLayerCommand extends Command {
this.parentLayer = null; // 父图层
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId);
// 如果parent 有值 或者 layer 上有parentId 则视为子图层的置顶
if (parent?.id) {
// 查找子图层索引
this.layerIndex = parent?.children?.findIndex(
(layer) => layer.id === this.layerId
);
this.layerIndex = parent?.children?.findIndex((layer) => layer.id === this.layerId);
this.parentLayer = parent;
} else {
// 查找图层索引
this.layerIndex = this.layers.value.findIndex(
(layer) => layer.id === this.layerId
);
this.layerIndex = this.layers.value.findIndex((layer) => layer.id === this.layerId);
}
this.layer = layer;
this.originalIndex = this.layerIndex;
// 目标位置
this.targetIndex =
options.direction === "up" ? this.layerIndex - 1 : this.layerIndex + 1;
this.targetIndex = options.direction === "up" ? this.layerIndex - 1 : this.layerIndex + 1;
}
async execute() {
@@ -668,9 +645,7 @@ export class ToggleLayerVisibilityCommand extends Command {
// 更新画布上图层对象的可见性
if (this.canvas) {
const layerObjects = this.canvas
.getObjects()
.filter((obj) => obj.layerId === this.layerId);
const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId);
layerObjects.forEach((obj) => {
obj.visible = this.layer.visible;
});
@@ -711,10 +686,7 @@ export class ToggleChildLayerVisibilityCommand extends Command {
this.layerManager = options.layerManager;
// 查找父图层和子图层
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId);
this.parentLayer = parent;
this.childLayer = layer;
@@ -731,9 +703,7 @@ export class ToggleChildLayerVisibilityCommand extends Command {
// 更新画布上图层对象的可见性
if (this.canvas) {
const layerObjects = this.canvas
.getObjects()
.filter((obj) => obj.layerId === this.layerId);
const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId);
layerObjects.forEach((obj) => {
obj.visible = this.childLayer.visible;
@@ -792,9 +762,7 @@ export class RenameLayerCommand extends Command {
// 更新图层对象上的图层名称
if (this.canvas) {
const layerObjects = this.canvas
.getObjects()
.filter((obj) => obj.layerId === this.layerId);
const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId);
layerObjects.forEach((obj) => {
obj.layerName = this.newName;
@@ -811,9 +779,7 @@ export class RenameLayerCommand extends Command {
// 恢复图层对象上的图层名称
if (this.canvas) {
const layerObjects = this.canvas
.getObjects()
.filter((obj) => obj.layerId === this.layerId);
const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId);
layerObjects.forEach((obj) => {
obj.layerName = this.oldName;
@@ -848,10 +814,7 @@ export class LayerLockCommand extends Command {
// 查找图层(包括子图层)
// 查找图层
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId);
this.layer = layer;
this.parentLayer = parent || null; // 父图层
this.oldLocked = this.layer ? this.layer.locked : null;
@@ -869,11 +832,7 @@ export class LayerLockCommand extends Command {
layer.locked = locked;
// 如果是组图层,递归更新所有子图层
if (
layer.type === "group" &&
layer.children &&
Array.isArray(layer.children)
) {
if (layer.type === "group" && layer.children && Array.isArray(layer.children)) {
layer.children.forEach((child) => {
this._updateLayerLockState(child, locked);
});
@@ -895,11 +854,7 @@ export class LayerLockCommand extends Command {
// 更新画布上对象的可选择状态
await this.layerManager?.updateLayersObjectsInteractivity();
console.log(
`${newLocked ? "锁定" : "解锁"}图层: ${this.layer.name} (ID: ${
this.layerId
})`
);
console.log(`${newLocked ? "锁定" : "解锁"}图层: ${this.layer.name} (ID: ${this.layerId})`);
return true;
}
@@ -911,9 +866,7 @@ export class LayerLockCommand extends Command {
// 更新画布上对象的可选择状态
await this.layerManager?.updateLayersObjectsInteractivity();
console.log(
`恢复图层锁定状态: ${this.layer.name} (锁定: ${this.oldLocked})`
);
console.log(`恢复图层锁定状态: ${this.layer.name} (锁定: ${this.oldLocked})`);
}
}
@@ -961,9 +914,7 @@ export class SetLayerOpacityCommand extends Command {
// 更新画布上对象的不透明度
if (this.canvas) {
const layerObjects = this.canvas
.getObjects()
.filter((obj) => obj.layerId === this.layerId);
const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId);
layerObjects.forEach((obj) => {
obj.opacity = this.opacity;
@@ -982,9 +933,7 @@ export class SetLayerOpacityCommand extends Command {
// 更新画布上对象的不透明度
if (this.canvas) {
const layerObjects = this.canvas
.getObjects()
.filter((obj) => obj.layerId === this.layerId);
const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId);
layerObjects.forEach((obj) => {
obj.opacity = this.oldOpacity;
@@ -1038,9 +987,7 @@ export class SetLayerBlendModeCommand extends Command {
// 更新画布上对象的混合模式
if (this.canvas) {
const layerObjects = this.canvas
.getObjects()
.filter((obj) => obj.layerId === this.layerId);
const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId);
layerObjects.forEach((obj) => {
obj.globalCompositeOperation = this.blendMode;
@@ -1059,9 +1006,7 @@ export class SetLayerBlendModeCommand extends Command {
// 更新画布上对象的混合模式
if (this.canvas) {
const layerObjects = this.canvas
.getObjects()
.filter((obj) => obj.layerId === this.layerId);
const layerObjects = this.canvas.getObjects().filter((obj) => obj.layerId === this.layerId);
layerObjects.forEach((obj) => {
obj.globalCompositeOperation = this.oldBlendMode;
@@ -1101,17 +1046,11 @@ export class MergeLayersCommand extends Command {
// 备份原图层
this.originalLayers = [...this.layers.value];
// 新图层ID
this.newLayerId = `merged_layer_${Date.now()}_${Math.floor(
Math.random() * 1000
)}`;
this.newLayerId = `merged_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
}
execute() {
if (
!this.layerIds ||
!Array.isArray(this.layerIds) ||
this.layerIds.length < 2
) {
if (!this.layerIds || !Array.isArray(this.layerIds) || this.layerIds.length < 2) {
console.error("合并图层至少需要两个图层");
return null;
}
@@ -1132,6 +1071,14 @@ export class MergeLayersCommand extends Command {
return null;
}
// 查找所有要组合的图层
const layersToGroup = this.layerIds
.map((id) => {
const tempLayer = this.layers.value.find((layer) => layer.id === id); // 需要给子图层加上parentId
return tempLayer ? { ...tempLayer, parentId: this.groupId } : false;
})
.filter(Boolean);
const topLayerIndex = Math.min(
...layersToGroup.map((layer) =>
this.layers.value.findIndex((fLayer) => fLayer.id === layer.id)
@@ -1164,9 +1111,7 @@ export class MergeLayersCommand extends Command {
});
// 移除原图层
this.layers.value = this.layers.value.filter(
(layer) => !this.layerIds.includes(layer.id)
);
this.layers.value = this.layers.value.filter((layer) => !this.layerIds.includes(layer.id));
// 插入新图层
this.layers.value.splice(topLayerIndex, 0, mergedLayer);
@@ -1227,18 +1172,13 @@ export class GroupLayersCommand extends Command {
this.originalLayers = [...this.layers.value];
// 新组ID
this.groupId =
generateId("group_layer_") ||
`group_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
generateId("group_layer_") || `group_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
this.originalActiveLayerId = this.activeLayerId.value; // 备份原活动图层ID
}
async execute() {
if (
!this.layerIds ||
!Array.isArray(this.layerIds) ||
this.layerIds.length < 2
) {
if (!this.layerIds || !Array.isArray(this.layerIds) || this.layerIds.length < 2) {
console.error("组合图层至少需要两个图层");
return null;
}
@@ -1282,9 +1222,7 @@ export class GroupLayersCommand extends Command {
});
// 移除原图层
this.layers.value = this.layers.value.filter(
(layer) => !this.layerIds.includes(layer.id)
);
this.layers.value = this.layers.value.filter((layer) => !this.layerIds.includes(layer.id));
// 插入新组图层
this.layers.value.splice(topLayerIndex, 0, groupLayer);
@@ -1338,9 +1276,7 @@ export class UngroupLayersCommand extends Command {
async execute() {
// 查找组图层
const groupIndex = this.layers.value.findIndex(
(layer) => layer.id === this.groupId
);
const groupIndex = this.layers.value.findIndex((layer) => layer.id === this.groupId);
if (groupIndex === -1) {
console.error(`找不到组图层 ${this.groupId}`);
@@ -1361,11 +1297,11 @@ export class UngroupLayersCommand extends Command {
this.layers.value.splice(
groupIndex,
1,
...groupLayer.children?.map((child) => {
...(groupLayer?.children?.map((child) => {
// 为子图层添加parentId
delete child.parentId; // 删除parentId属性
return { ...child };
})
}) || {})
);
// 更新当前活动图层为第一个子图层
if (this.childLayerIds.length > 0 && this.activeLayerId) {
@@ -1411,18 +1347,14 @@ export class MergeLayerObjectsCommand extends Command {
// 备份原始对象,用于撤销
if (this.activeLayer && Array.isArray(this.activeLayer.fabricObjects)) {
this.originalObjects =
this.canvas
?.getObjects()
?.filter((fItem) => fItem.layerId === this.activeLayer.id) || [];
this.canvas?.getObjects()?.filter((fItem) => fItem.layerId === this.activeLayer.id) || [];
} else {
this.originalObjects = [];
}
// 新合并图像对象
this.mergedImage = null;
this.newImageId = `merged_image_${Date.now()}_${Math.floor(
Math.random() * 1000
)}`;
this.newImageId = `merged_image_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
}
async execute() {
@@ -1458,10 +1390,7 @@ export class MergeLayerObjectsCommand extends Command {
// 异步处理图像合并
try {
const mergedImage = await this._createMergedImageAsync(
objectsToMerge,
bounds
);
const mergedImage = await this._createMergedImageAsync(objectsToMerge, bounds);
this._setupMergedImage(mergedImage, bounds);
this._replaceObjects(mergedImage);
@@ -1611,9 +1540,7 @@ export class MergeLayerObjectsCommand extends Command {
});
tempCanvas.add(clonedObj);
console.log(
`添加对象 ${index + 1}/${objects.length}: ${obj.type || "unknown"}`
);
console.log(`添加对象 ${index + 1}/${objects.length}: ${obj.type || "unknown"}`);
} catch (error) {
console.error(`添加对象到临时画布时发生错误:`, error);
}
@@ -1672,10 +1599,7 @@ export class MergeLayerObjectsCommand extends Command {
});
// 如果有新的图像对象,也要移除
if (
this.fabricImage &&
this.canvas.getObjects().includes(this.fabricImage)
) {
if (this.fabricImage && this.canvas.getObjects().includes(this.fabricImage)) {
this.canvas.remove(this.fabricImage);
}
@@ -1691,9 +1615,7 @@ export class MergeLayerObjectsCommand extends Command {
// 更新缩略图
if (this.canvas.thumbnailManager) {
setTimeout(() => {
this.canvas.thumbnailManager.generateLayerThumbnail(
this.activeLayer.id
);
this.canvas.thumbnailManager.generateLayerThumbnail(this.activeLayer.id);
}, 100);
}
}
@@ -1737,9 +1659,7 @@ export class MergeLayerObjectsCommand extends Command {
// 更新缩略图
if (this.canvas.thumbnailManager) {
setTimeout(() => {
this.canvas.thumbnailManager.generateLayerThumbnail(
this.activeLayer.id
);
this.canvas.thumbnailManager.generateLayerThumbnail(this.activeLayer.id);
}, 100);
}
}
@@ -1827,8 +1747,7 @@ export class LayerObjectsToGroupCommand extends Command {
this.existingGroupId = null;
this.groupObjectId = null;
this.newGroupId =
generateId("group") ||
`group_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
generateId("group") || `group_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
this.wasGroupCreated = false;
// 保存原始位置信息
@@ -1889,10 +1808,7 @@ export class LayerObjectsToGroupCommand extends Command {
const newObjectsToAdd = [];
if (this.fabricImage) {
// 如果是重做,恢复新对象的原始位置
if (
!this.isFirstExecution &&
this.newObjectPositions.has(this.fabricImage.id)
) {
if (!this.isFirstExecution && this.newObjectPositions.has(this.fabricImage.id)) {
const savedPosition = this.newObjectPositions.get(this.fabricImage.id);
this.fabricImage.set({
left: savedPosition.left,
@@ -1905,9 +1821,7 @@ export class LayerObjectsToGroupCommand extends Command {
flipX: savedPosition.flipX,
flipY: savedPosition.flipY,
});
console.log(
`🔄 重做时恢复新对象位置: (${savedPosition.left}, ${savedPosition.top})`
);
console.log(`🔄 重做时恢复新对象位置: (${savedPosition.left}, ${savedPosition.top})`);
}
newObjectsToAdd.push(this.fabricImage);
@@ -1960,9 +1874,7 @@ export class LayerObjectsToGroupCommand extends Command {
if (!this.existingGroupId) {
const groupObjects =
this.activeLayer.fabricObjects.find(
(obj) => obj && obj.type === "group"
) || null;
this.activeLayer.fabricObjects.find((obj) => obj && obj.type === "group") || null;
this.existingGroupId = groupObjects?.id || null;
}
@@ -2062,9 +1974,7 @@ export class LayerObjectsToGroupCommand extends Command {
// 更新图层的对象列表
// this.activeLayer.fabricObjects = [groupObject];
this.activeLayer.fabricObjects = [
groupObject.toObject(["id", "layerId", "layerName"]),
];
this.activeLayer.fabricObjects = [groupObject.toObject(["id", "layerId", "layerName"])];
}
/**
@@ -2150,10 +2060,7 @@ export class LayerObjectsToGroupCommand extends Command {
restorePosition = { ...originalPosition };
} else {
// 计算对象在画布中的当前绝对位置
restorePosition = this._calculateObjectAbsolutePosition(
obj,
groupObject
);
restorePosition = this._calculateObjectAbsolutePosition(obj, groupObject);
}
objectsToRestore.push({
@@ -2208,9 +2115,7 @@ export class LayerObjectsToGroupCommand extends Command {
restoredObjects.push(obj);
console.log(
`✅ 恢复原始对象 ${obj.id || obj.type} 到位置 (${position.left}, ${
position.top
})`
`✅ 恢复原始对象 ${obj.id || obj.type} 到位置 (${position.left}, ${position.top})`
);
} catch (error) {
console.error(`恢复对象 ${obj.id || obj.type} 时发生错误:`, error);
@@ -2270,10 +2175,7 @@ export class LayerObjectsToGroupCommand extends Command {
const objectPoint = new fabric.Point(obj.left || 0, obj.top || 0);
// 应用组的变换矩阵计算绝对位置
const absolutePoint = fabric.util.transformPoint(
objectPoint,
groupTransform
);
const absolutePoint = fabric.util.transformPoint(objectPoint, groupTransform);
// 计算缩放比例
const totalScaleX = (group.scaleX || 1) * (obj.scaleX || 1);
@@ -2441,8 +2343,7 @@ export class CreateImageLayerCommand extends Command {
// 存储执行过程中的结果
this.newLayerId =
generateId("layer_image_") ||
`layer_image_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
generateId("layer_image_") || `layer_image_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
this.commands = [];
this.executedCommands = [];
}
@@ -2457,8 +2358,7 @@ export class CreateImageLayerCommand extends Command {
this.executedCommands = [];
// 生成图层名称
const fileName =
this.layerName || `图片 ${new Date().toLocaleTimeString()}`;
const fileName = this.layerName || `图片 ${new Date().toLocaleTimeString()}`;
this.fabricImage.set({
id: this.imageId,
@@ -2529,9 +2429,7 @@ export class CreateImageLayerCommand extends Command {
return true;
}
console.log(
`↩️ 开始撤销创建图片图层操作,共 ${this.executedCommands.length} 个子命令`
);
console.log(`↩️ 开始撤销创建图片图层操作,共 ${this.executedCommands.length} 个子命令`);
try {
// 逆序撤销已执行的命令
@@ -2547,10 +2445,7 @@ export class CreateImageLayerCommand extends Command {
}
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
} catch (error) {
console.error(
`❌ 子命令撤销失败: ${command.constructor.name}`,
error
);
console.error(`❌ 子命令撤销失败: ${command.constructor.name}`, error);
}
}
}
@@ -2583,10 +2478,7 @@ export class CreateImageLayerCommand extends Command {
}
console.log(`✅ 子命令回滚成功: ${command.constructor.name}`);
} catch (error) {
console.error(
`❌ 子命令回滚失败: ${command.constructor.name}`,
error
);
console.error(`❌ 子命令回滚失败: ${command.constructor.name}`, error);
}
}
}
@@ -2721,9 +2613,7 @@ export class ReorderChildLayersCommand extends Command {
async execute() {
// 查找父图层
const parentLayer = this.layers.value.find(
(layer) => layer.id === this.parentId
);
const parentLayer = this.layers.value.find((layer) => layer.id === this.parentId);
if (!parentLayer) return false;
// 获取子图层
@@ -2873,15 +2763,11 @@ export class CutLayerCommand extends Command {
// 生成新图层ID和名称
this.newLayerId =
generateId("layer_") ||
`layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
generateId("layer_") || `layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
}
async execute() {
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId);
const sourceLayer = layer;
const parentLayer = parent;
@@ -2908,9 +2794,7 @@ export class CutLayerCommand extends Command {
blendMode: sourceLayer.blendMode,
fabricObjects: [],
children: sourceLayer.children ? [...sourceLayer.children] : [],
layerProperties: sourceLayer.layerProperties
? { ...sourceLayer.layerProperties }
: {},
layerProperties: sourceLayer.layerProperties ? { ...sourceLayer.layerProperties } : {},
metadata: sourceLayer.metadata ? { ...sourceLayer.metadata } : {},
parentId: parentLayer ? parentLayer.id : undefined, // 保持父图层关系
});
@@ -2920,11 +2804,8 @@ export class CutLayerCommand extends Command {
if (parentLayer) {
// 处理子图层在父图层的children数组中插入
const sourceChildIndex = parentLayer.children.findIndex(
(child) => child.id === this.layerId
);
insertIndex =
this.insertIndex !== null ? this.insertIndex : sourceChildIndex + 1;
const sourceChildIndex = parentLayer.children.findIndex((child) => child.id === this.layerId);
insertIndex = this.insertIndex !== null ? this.insertIndex : sourceChildIndex + 1;
// 插入到父图层的children数组中
parentLayer.children.splice(insertIndex, 0, this.newLayer);
@@ -2933,11 +2814,8 @@ export class CutLayerCommand extends Command {
this.childInsertIndex = insertIndex;
} else {
// 处理主图层:在主图层数组中插入
const sourceIndex = this.layers.value.findIndex(
(l) => l.id === this.layerId
);
insertIndex =
this.insertIndex !== null ? this.insertIndex : sourceIndex + 1;
const sourceIndex = this.layers.value.findIndex((l) => l.id === this.layerId);
insertIndex = this.insertIndex !== null ? this.insertIndex : sourceIndex + 1;
// 插入到主图层数组中
this.layers.value.splice(insertIndex, 0, this.newLayer);
@@ -2956,9 +2834,7 @@ export class CutLayerCommand extends Command {
// 重新渲染画布
await this.layerManager?.updateLayersObjectsInteractivity(false);
console.log(
`已复制图层:${newName} ${this.isChildLayer ? "(子图层)" : "(主图层)"}`
);
console.log(`已复制图层:${newName} ${this.isChildLayer ? "(子图层)" : "(主图层)"}`);
return this.newLayerId;
}
@@ -2975,9 +2851,7 @@ export class CutLayerCommand extends Command {
}
} else {
// 撤销主图层:从主图层数组中删除
const index = this.layers.value.findIndex(
(l) => l.id === this.newLayerId
);
const index = this.layers.value.findIndex((l) => l.id === this.newLayerId);
if (index !== -1) {
this.layers.value.splice(index, 1);
}
@@ -3007,9 +2881,7 @@ export class CutLayerCommand extends Command {
fabric.util.enlivenObjects(serializedObjects, (objects) => {
objects.forEach((obj) => {
// 生成新的对象ID
const newObjId = `obj_${Date.now()}_${Math.floor(
Math.random() * 1000
)}`;
const newObjId = `obj_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
obj.id = newObjId;
obj.layerId = this.newLayerId;
obj.layerName = this.newLayer.name;
@@ -3066,8 +2938,7 @@ export class CreateAdjustmentLayerCommand extends Command {
this.newLayer = null;
// 生成新图层ID
this.newLayerId =
generateId("adj_layer_") ||
`adj_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
generateId("adj_layer_") || `adj_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
}
execute() {
@@ -3089,9 +2960,7 @@ export class CreateAdjustmentLayerCommand extends Command {
// 计算插入位置
const insertIndex =
this.insertIndex !== null
? this.insertIndex
: this._getInsertIndexAboveActiveLayer();
this.insertIndex !== null ? this.insertIndex : this._getInsertIndexAboveActiveLayer();
// 插入新图层
this.layers.value.splice(insertIndex, 0, this.newLayer);
@@ -3155,9 +3024,7 @@ export class ApplyLayerStyleCommand extends Command {
return;
}
this.layer = layer;
this.oldStyles = this.layer
? { ...(this.layer.layerProperties?.styles || {}) }
: {};
this.oldStyles = this.layer ? { ...(this.layer.layerProperties?.styles || {}) } : {};
}
execute() {
@@ -3296,9 +3163,7 @@ export class LayerClippingMaskCommand extends Command {
if (this.maskLayerId) {
// 创建剪贴蒙版
const maskLayer = this.layers.value.find(
(l) => l.id === this.maskLayerId
);
const maskLayer = this.layers.value.find((l) => l.id === this.maskLayerId);
if (!maskLayer) {
console.error(`蒙版图层 ${this.maskLayerId} 不存在`);
return false;
@@ -3525,19 +3390,14 @@ export class ChangeFixedImageCommand extends Command {
this.retryCount = attempt;
if (attempt === this.maxRetries) {
throw new Error(
`图像加载失败,已重试${this.maxRetries}次: ${error.message}`
);
throw new Error(`图像加载失败,已重试${this.maxRetries}次: ${error.message}`);
}
// 指数退避重试
const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
console.warn(
`图像加载重试 ${attempt + 1}/${this.maxRetries}:`,
error.message
);
console.warn(`图像加载重试 ${attempt + 1}/${this.maxRetries}:`, error.message);
}
}
}
@@ -3545,9 +3405,7 @@ export class ChangeFixedImageCommand extends Command {
loadImage() {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(
new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)
);
reject(new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`));
}, this.timeoutMs);
fabric.Image.fromURL(
@@ -3585,15 +3443,9 @@ export class ChangeFixedImageCommand extends Command {
// 移除旧对象(如果存在)
if (this.previousObjectId) {
const { object: oldObject } = findObjectById(
this.canvas,
this.previousObjectId
);
const { object: oldObject } = findObjectById(this.canvas, this.previousObjectId);
if (oldObject) {
const removeSuccess = removeCanvasObjectByObject(
this.canvas,
oldObject
);
const removeSuccess = removeCanvasObjectByObject(this.canvas, oldObject);
if (!removeSuccess) {
console.warn("移除旧对象失败,但继续执行");
}
@@ -3650,97 +3502,75 @@ export class ChangeFixedImageCommand extends Command {
}
return new Promise((resolve, reject) => {
fabric.util.enlivenObjects(
[this.previousImage.objectData],
async (objects) => {
try {
const restoredImage = objects[0];
fabric.util.enlivenObjects([this.previousImage.objectData], async (objects) => {
try {
const restoredImage = objects[0];
await optimizeCanvasRendering(this.canvas, async () => {
// 移除当前对象
if (this.newObjectId) {
const { object: currentObject } = findObjectById(
this.canvas,
this.newObjectId
);
if (currentObject) {
removeCanvasObjectByObject(this.canvas, currentObject);
}
await optimizeCanvasRendering(this.canvas, async () => {
// 移除当前对象
if (this.newObjectId) {
const { object: currentObject } = findObjectById(this.canvas, this.newObjectId);
if (currentObject) {
removeCanvasObjectByObject(this.canvas, currentObject);
}
}
// 恢复之前的变换
if (this.previousTransform) {
restoredImage.set(this.previousTransform);
}
// 恢复之前的变换
if (this.previousTransform) {
restoredImage.set(this.previousTransform);
}
// 设置图层属性
restoredImage.set({
id: this.previousObjectId,
layerId: this.targetLayer.id,
layerName: this.targetLayer.name,
isBackground: this.targetLayer.isBackground,
isFixed: this.targetLayer.isFixed,
});
// 使用帮助函数在正确的z-index位置恢复对象
if (
this.previousZIndex !== undefined &&
this.previousZIndex >= 0
) {
const insertSuccess = insertObjectAtZIndex(
this.canvas,
restoredImage,
this.previousZIndex,
false
);
if (insertSuccess) {
console.log(`恢复图像到z-index位置: ${this.previousZIndex}`);
} else {
// 如果插入失败,回退到普通添加
this.canvas.add(restoredImage);
console.log("z-index恢复失败恢复图像添加到顶层");
}
} else {
// 如果没有保存的z-index添加到顶层
this.canvas.add(restoredImage);
console.log("恢复图像添加到顶层");
}
restoredImage.setCoords();
// 更新引用
this.targetLayer.fabricObject = restoredImage.toObject([
"id",
"layerId",
"type",
]);
this.layerManager.updateLayerObject(
this.targetLayer.id,
restoredImage
);
// 设置图层属性
restoredImage.set({
id: this.previousObjectId,
layerId: this.targetLayer.id,
layerName: this.targetLayer.name,
isBackground: this.targetLayer.isBackground,
isFixed: this.targetLayer.isFixed,
});
resolve();
} catch (error) {
reject(error);
}
// 使用帮助函数在正确的z-index位置恢复对象
if (this.previousZIndex !== undefined && this.previousZIndex >= 0) {
const insertSuccess = insertObjectAtZIndex(
this.canvas,
restoredImage,
this.previousZIndex,
false
);
if (insertSuccess) {
console.log(`恢复图像到z-index位置: ${this.previousZIndex}`);
} else {
// 如果插入失败,回退到普通添加
this.canvas.add(restoredImage);
console.log("z-index恢复失败恢复图像添加到顶层");
}
} else {
// 如果没有保存的z-index添加到顶层
this.canvas.add(restoredImage);
console.log("恢复图像添加到顶层");
}
restoredImage.setCoords();
// 更新引用
this.targetLayer.fabricObject = restoredImage.toObject(["id", "layerId", "type"]);
this.layerManager.updateLayerObject(this.targetLayer.id, restoredImage);
});
resolve();
} catch (error) {
reject(error);
}
);
});
});
}
async removeCurrentImage() {
await optimizeCanvasRendering(this.canvas, async () => {
if (this.newObjectId) {
const { object: currentObject } = findObjectById(
this.canvas,
this.newObjectId
);
const { object: currentObject } = findObjectById(this.canvas, this.newObjectId);
if (currentObject) {
const removeSuccess = removeCanvasObjectByObject(
this.canvas,
currentObject
);
const removeSuccess = removeCanvasObjectByObject(this.canvas, currentObject);
if (removeSuccess) {
this.targetLayer.fabricObject = null;
this.layerManager.updateLayerObject(this.targetLayer.id, null);
@@ -3797,10 +3627,7 @@ export class AddImageToLayerCommand extends Command {
this.validateInputs();
// 查找目标图层
const { layer } = findLayerRecursively(
this.layerManager?.layers?.value || [],
this.layerId
);
const { layer } = findLayerRecursively(this.layerManager?.layers?.value || [], this.layerId);
this.targetLayer = layer;
@@ -3859,10 +3686,7 @@ export class AddImageToLayerCommand extends Command {
this.canvas.remove(this.addedObject);
// 从图层管理器中移除
this.layerManager.removeObjectFromLayer(
this.addedObject.id,
this.layerId
);
this.layerManager.removeObjectFromLayer(this.addedObject.id, this.layerId);
this.isExecuted = false;
@@ -3925,19 +3749,14 @@ export class AddImageToLayerCommand extends Command {
this.retryCount = attempt;
if (attempt === this.maxRetries) {
throw new Error(
`图像加载失败,已重试${this.maxRetries}次: ${error.message}`
);
throw new Error(`图像加载失败,已重试${this.maxRetries}次: ${error.message}`);
}
// 指数退避重试
const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
console.warn(
`图像加载重试 ${attempt + 1}/${this.maxRetries}:`,
error.message
);
console.warn(`图像加载重试 ${attempt + 1}/${this.maxRetries}:`, error.message);
}
}
}
@@ -3945,9 +3764,7 @@ export class AddImageToLayerCommand extends Command {
loadImage() {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(
new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)
);
reject(new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`));
}, this.timeoutMs);
fabric.Image.fromURL(
@@ -4057,9 +3874,7 @@ export class RemoveChildLayerCommand extends Command {
this.parentLayer = parent;
this.childIndex =
this.parentLayer?.children?.findIndex(
(child) => child.id === this.layerId
) ?? -1;
this.parentLayer?.children?.findIndex((child) => child.id === this.layerId) ?? -1;
this.removedChild = this.parentLayer?.children?.[this.childIndex];
this.isActiveLayer = this.layerId === this.activeLayerId.value;
@@ -4074,10 +3889,7 @@ export class RemoveChildLayerCommand extends Command {
}
// 从画布中移除子图层中的所有对象
if (
this.removedChild.fabricObjects &&
this.removedChild.fabricObjects.length > 0
) {
if (this.removedChild.fabricObjects && this.removedChild.fabricObjects.length > 0) {
this.removedChild.fabricObjects.forEach((obj) => {
const { object } = findObjectById(this.canvas, obj.id);
if (object) {
@@ -4096,9 +3908,7 @@ export class RemoveChildLayerCommand extends Command {
this.activeLayerId.value = this.parentLayer.children[0].id;
} else {
this.activeLayerId.value =
this.layers.value.find(
(layer) => !layer.isBackground || !layer.isFixed
)?.[0]?.id || null;
this.layers.value.find((layer) => !layer.isBackground || !layer.isFixed)?.[0]?.id || null;
}
}
@@ -4233,10 +4043,7 @@ export class ChildLayerLockCommand extends Command {
this.layerManager = options.layerManager;
// 查找父图层和子图层
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId);
this.parentLayer = parent;
this.childLayer = layer;
this.oldLocked = this.childLayer ? this.childLayer.locked : null;

View File

@@ -21,9 +21,7 @@ export class LiquifyCommand extends Command {
constructor(options) {
super({
name: options.name || `液化操作: ${options.mode || "未知模式"}`,
description:
options.description ||
`使用${options.mode || "未知模式"}模式进行液化操作`,
description: options.description || `使用${options.mode || "未知模式"}模式进行液化操作`,
});
this.canvas = options.canvas;
@@ -96,9 +94,7 @@ export class LiquifyCommand extends Command {
// 对于图像对象我们需要保存src和元数据
const state = {
src: this.targetObject.getSrc ? this.targetObject.getSrc() : null,
element: this.targetObject._element
? this.targetObject._element.cloneNode(true)
: null,
element: this.targetObject._element ? this.targetObject._element.cloneNode(true) : null,
filters: this.targetObject.filters ? [...this.targetObject.filters] : [],
originalData: this.originalData,
targetLayerId: this.targetLayerId,
@@ -172,10 +168,7 @@ export class LiquifyCommand extends Command {
// 确保图层引用更新
const layer = this.layerManager.getLayerById(this.targetLayerId);
if (layer) {
if (
layer.type === "background" &&
layer.fabricObject === this.targetObject
) {
if (layer.type === "background" && layer.fabricObject === this.targetObject) {
layer.fabricObject = img;
} else if (layer.fabricObjects) {
const objIndex = layer.fabricObjects.indexOf(this.targetObject);
@@ -251,9 +244,7 @@ export class LiquifyDeformCommand extends LiquifyCommand {
super({
...options,
name: `液化变形: ${options.mode || "未知模式"}`,
description: `在(${options.x}, ${options.y})应用${
options.mode || "未知模式"
}变形`,
description: `在(${options.x}, ${options.y})应用${options.mode || "未知模式"}变形`,
});
this.x = options.x;
@@ -343,9 +334,7 @@ export class LiquifyDeformCommand extends LiquifyCommand {
layer.fabricObject = img;
} else if (layer.fabricObjects) {
const objIndex = layer.fabricObjects.findIndex(
(obj) =>
obj === this.savedState?.originalObject ||
obj === this.targetObject
(obj) => obj === this.savedState?.originalObject || obj === this.targetObject
);
if (objIndex !== -1) {
layer.fabricObjects[objIndex] = img;
@@ -374,8 +363,7 @@ export class CompositeLiquifyCommand extends Command {
constructor(options) {
super({
name: options.name || "液化操作组合",
description:
options.description || `包含${options.commands?.length || 0}个液化操作`,
description: options.description || `包含${options.commands?.length || 0}个液化操作`,
});
this.commands = options.commands || [];
@@ -551,10 +539,7 @@ export class LiquifyStateCommand extends Command {
}
// 通过引用管理器更新图像数据
await this.refManager.updateObjectImageData(
this.objectRefId,
this.finalImageData
);
await this.refManager.updateObjectImageData(this.objectRefId, this.finalImageData);
// 恢复液化管理器到最终状态
if (this.liquifyManager && this.finalLiquifyState) {
@@ -585,10 +570,7 @@ export class LiquifyStateCommand extends Command {
}
// 通过引用管理器恢复到初始快照
await this.refManager.restoreFromSnapshot(
this.objectRefId,
this.initialSnapshotId
);
await this.refManager.restoreFromSnapshot(this.objectRefId, this.initialSnapshotId);
// 恢复液化管理器到初始状态 - 关键修复
if (this.liquifyManager) {
@@ -604,18 +586,16 @@ export class LiquifyStateCommand extends Command {
// 强制设置原始图像数据为初始状态
if (this.liquifyManager.enhancedManager) {
this.liquifyManager.enhancedManager.originalImageData =
this.initialImageData;
this.liquifyManager.enhancedManager.currentImageData =
this._cloneImageData(this.initialImageData);
this.liquifyManager.enhancedManager.originalImageData = this.initialImageData;
this.liquifyManager.enhancedManager.currentImageData = this._cloneImageData(
this.initialImageData
);
// 重置激活渲染器的数据
if (this.liquifyManager.enhancedManager.activeRenderer) {
const renderer = this.liquifyManager.enhancedManager.activeRenderer;
renderer.originalImageData = this.initialImageData;
renderer.currentImageData = this._cloneImageData(
this.initialImageData
);
renderer.currentImageData = this._cloneImageData(this.initialImageData);
// 重置CPU渲染器的网格和变形历史
if (renderer.reset) {
@@ -741,8 +721,7 @@ export class LiquifyStateCommand extends Command {
timestamp: Date.now(),
properties: this.refManager._captureObjectState(fabricObject),
imageData: this.initialImageData,
eventListeners:
this.refManager.eventListeners.get(this.objectRefId) || {},
eventListeners: this.refManager.eventListeners.get(this.objectRefId) || {},
};
this.refManager.stateSnapshots.set(this.initialSnapshotId, snapshot);
@@ -772,8 +751,7 @@ export class LiquifyStateCommand extends Command {
timestamp: Date.now(),
properties: this.refManager._captureObjectState(fabricObject),
imageData: this.finalImageData,
eventListeners:
this.refManager.eventListeners.get(this.objectRefId) || {},
eventListeners: this.refManager.eventListeners.get(this.objectRefId) || {},
};
this.refManager.stateSnapshots.set(this.finalSnapshotId, snapshot);
@@ -804,8 +782,7 @@ export class LiquifyStateCommand extends Command {
// 捕获当前渲染器状态
activeRendererState: null,
// 捕获目标对象引用
targetObjectRef:
this.liquifyManager.targetObject ?? enhancedManager.targetObject,
targetObjectRef: this.liquifyManager.targetObject ?? enhancedManager.targetObject,
// 捕获初始化状态
initialized: this.liquifyManager.initialized || false,
};
@@ -832,13 +809,9 @@ export class LiquifyStateCommand extends Command {
params: { ...renderer.params },
currentMode: renderer.currentMode,
// 对于CPU渲染器还需要保存网格状态
meshState: renderer.mesh
? this._captureMeshState(renderer.mesh)
: null,
meshState: renderer.mesh ? this._captureMeshState(renderer.mesh) : null,
// 保存变形历史
deformHistory: renderer.deformHistory
? [...renderer.deformHistory]
: [],
deformHistory: renderer.deformHistory ? [...renderer.deformHistory] : [],
};
}
}
@@ -871,9 +844,7 @@ export class LiquifyStateCommand extends Command {
this.realtimeUpdater.targetObject = this.liquifyManager.targetObject;
// 如果有setTargetObject方法调用它
if (typeof this.realtimeUpdater.setTargetObject === "function") {
this.realtimeUpdater.setTargetObject(
this.liquifyManager.targetObject
);
this.realtimeUpdater.setTargetObject(this.liquifyManager.targetObject);
}
}
@@ -911,10 +882,8 @@ export class LiquifyStateCommand extends Command {
} else if (renderer.mesh && renderer._initMesh) {
// 如果没有保存的网格状态,重新初始化网格
renderer._initMesh(
rendererState.originalImageData?.width ||
renderer.originalImageData?.width,
rendererState.originalImageData?.height ||
renderer.originalImageData?.height
rendererState.originalImageData?.width || renderer.originalImageData?.width,
rendererState.originalImageData?.height || renderer.originalImageData?.height
);
}
@@ -978,13 +947,7 @@ export class LiquifyStateCommand extends Command {
* @private
*/
_restoreMeshState(mesh, meshState) {
if (
!mesh ||
!meshState ||
!Array.isArray(mesh) ||
!Array.isArray(meshState)
)
return;
if (!mesh || !meshState || !Array.isArray(mesh) || !Array.isArray(meshState)) return;
for (let i = 0; i < Math.min(mesh.length, meshState.length); i++) {
for (let j = 0; j < Math.min(mesh[i].length, meshState[i].length); j++) {
@@ -1043,8 +1006,7 @@ export class BatchLiquifyStateCommand extends Command {
constructor(options) {
super({
name: options.name || "批量液化操作",
description:
options.description || `批量液化${options.commands?.length || 0}个对象`,
description: options.description || `批量液化${options.commands?.length || 0}个对象`,
});
this.commands = options.commands || [];
@@ -1117,9 +1079,7 @@ export class BatchLiquifyStateCommand extends Command {
* @returns {Boolean} 是否有效
*/
isValid() {
return (
this.commands.length > 0 && this.commands.every((cmd) => cmd.isValid())
);
return this.commands.length > 0 && this.commands.every((cmd) => cmd.isValid());
}
/**

View File

@@ -34,11 +34,7 @@ export class SetActiveLayerCommand extends Command {
}
execute() {
const { layer } = findLayerRecursively(
this.layers.value,
this.layerId,
this.parentId
);
const { layer } = findLayerRecursively(this.layers.value, this.layerId, this.parentId);
this.newLayer = layer;
if (!this.newLayer) {
@@ -68,10 +64,7 @@ export class SetActiveLayerCommand extends Command {
this.canvas.discardActiveObject();
// 设置为新的图层下的对象为激活,但需要确保对象存在于画布上
if (
this.newLayer.fabricObjects &&
this.newLayer.fabricObjects.length > 0
) {
if (this.newLayer.fabricObjects && this.newLayer.fabricObjects.length > 0) {
const canvasObjects = this.canvas.getObjects();
const validObjects = this.newLayer.fabricObjects.filter(
(obj) => obj && canvasObjects.includes(obj)
@@ -153,9 +146,7 @@ export class AddObjectToLayerCommand extends Command {
this.layerManager = options.layerManager;
// 保存对象原始状态和ID
this.objectId =
this.fabricObject.id ||
`obj_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
this.objectId = this.fabricObject.id || `obj_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
this.fabricObject.set({
id: this.objectId,
layerId: options.layerId,
@@ -163,11 +154,7 @@ export class AddObjectToLayerCommand extends Command {
});
// 保存完整的对象状态,包括位置和变换信息
this.originalObjectState = this.fabricObject.toObject([
"id",
"layerId",
"layerName",
]);
this.originalObjectState = this.fabricObject.toObject(["id", "layerId", "layerName"]);
// // 新增:保存详细的位置和变换信息,用于重做时恢复
// this.originalPosition = {
@@ -279,8 +266,7 @@ export class AddObjectToLayerCommand extends Command {
// 将对象添加到图层的fabricObjects数组
layer.fabricObjects = layer.fabricObjects || [];
layer.fabricObjects.push(
this.fabricObject?.toObject?.(["id", "layerId", "layerName"]) ||
this.fabricObject
this.fabricObject?.toObject?.(["id", "layerId", "layerName"]) || this.fabricObject
);
await this.layerManager?.updateLayersObjectsInteractivity?.(false);
@@ -327,16 +313,12 @@ export class AddObjectToLayerCommand extends Command {
// 从图层的fabricObjects数组中移除对象
if (layer.fabricObjects) {
layer.fabricObjects = layer.fabricObjects.filter(
(obj) => obj.id !== this.objectId
);
layer.fabricObjects = layer.fabricObjects.filter((obj) => obj.id !== this.objectId);
}
// 优化渲染 - 统一批处理 支持异步回调
optimizeCanvasRendering(this.canvas, async () => {
// 从画布移除对象
const object = this.canvas
.getObjects()
.find((obj) => obj.id === this.objectId);
const object = this.canvas.getObjects().find((obj) => obj.id === this.objectId);
if (object) {
// 先丢弃活动对象,避免控制点渲染错误
this.canvas.discardActiveObject();
@@ -410,9 +392,7 @@ export class RemoveObjectFromLayerCommand extends Command {
// 从图层的fabricObjects数组移除对象
if (layer.fabricObjects) {
layer.fabricObjects = layer.fabricObjects.filter(
(obj) => obj.id !== this.objectId
);
layer.fabricObjects = layer.fabricObjects.filter((obj) => obj.id !== this.objectId);
}
// 更新画布
@@ -639,19 +619,14 @@ export class ChangeFixedImageCommand extends Command {
this.retryCount = attempt;
if (attempt === this.maxRetries) {
throw new Error(
`图像加载失败,已重试${this.maxRetries}次: ${error.message}`
);
throw new Error(`图像加载失败,已重试${this.maxRetries}次: ${error.message}`);
}
// 指数退避重试
const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
console.warn(
`图像加载重试 ${attempt + 1}/${this.maxRetries}:`,
error.message
);
console.warn(`图像加载重试 ${attempt + 1}/${this.maxRetries}:`, error.message);
}
}
}
@@ -659,9 +634,7 @@ export class ChangeFixedImageCommand extends Command {
loadImage() {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(
new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)
);
reject(new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`));
}, this.timeoutMs);
fabric.Image.fromURL(
@@ -697,15 +670,9 @@ export class ChangeFixedImageCommand extends Command {
// 移除旧对象(如果存在)
if (this.previousObjectId) {
const { object: oldObject } = findObjectById(
this.canvas,
this.previousObjectId
);
const { object: oldObject } = findObjectById(this.canvas, this.previousObjectId);
if (oldObject) {
const removeSuccess = removeCanvasObjectByObject(
this.canvas,
oldObject
);
const removeSuccess = removeCanvasObjectByObject(this.canvas, oldObject);
if (!removeSuccess) {
console.warn("移除旧对象失败,但继续执行");
}
@@ -771,93 +738,75 @@ export class ChangeFixedImageCommand extends Command {
}
return new Promise((resolve, reject) => {
fabric.util.enlivenObjects(
[this.previousImage.objectData],
async (objects) => {
try {
const restoredImage = objects[0];
fabric.util.enlivenObjects([this.previousImage.objectData], async (objects) => {
try {
const restoredImage = objects[0];
await optimizeCanvasRendering(this.canvas, async () => {
// 移除当前对象
if (this.newObjectId) {
const { object: currentObject } = findObjectById(
this.canvas,
this.newObjectId
);
if (currentObject) {
removeCanvasObjectByObject(this.canvas, currentObject);
}
await optimizeCanvasRendering(this.canvas, async () => {
// 移除当前对象
if (this.newObjectId) {
const { object: currentObject } = findObjectById(this.canvas, this.newObjectId);
if (currentObject) {
removeCanvasObjectByObject(this.canvas, currentObject);
}
}
// 恢复之前的变换
if (this.previousTransform) {
restoredImage.set(this.previousTransform);
}
// 恢复之前的变换
if (this.previousTransform) {
restoredImage.set(this.previousTransform);
}
// 设置图层属性
restoredImage.set({
id: this.previousObjectId,
layerId: this.targetLayer.id,
layerName: this.targetLayer.name,
isBackground: this.targetLayer.isBackground,
isFixed: this.targetLayer.isFixed,
});
// 使用帮助函数在正确的z-index位置恢复对象
if (
this.previousZIndex !== undefined &&
this.previousZIndex >= 0
) {
const insertSuccess = insertObjectAtZIndex(
this.canvas,
restoredImage,
this.previousZIndex,
false
);
if (insertSuccess) {
console.log(`恢复图像到z-index位置: ${this.previousZIndex}`);
} else {
// 如果插入失败,回退到普通添加
this.canvas.add(restoredImage);
console.log("z-index恢复失败恢复图像添加到顶层");
}
} else {
// 如果没有保存的z-index添加到顶层
this.canvas.add(restoredImage);
console.log("恢复图像添加到顶层");
}
restoredImage.setCoords();
// 更新引用
this.targetLayer.fabricObject = restoredImage;
this.layerManager.updateLayerObject(
this.targetLayer.id,
restoredImage
);
// 设置图层属性
restoredImage.set({
id: this.previousObjectId,
layerId: this.targetLayer.id,
layerName: this.targetLayer.name,
isBackground: this.targetLayer.isBackground,
isFixed: this.targetLayer.isFixed,
});
resolve();
} catch (error) {
reject(error);
}
// 使用帮助函数在正确的z-index位置恢复对象
if (this.previousZIndex !== undefined && this.previousZIndex >= 0) {
const insertSuccess = insertObjectAtZIndex(
this.canvas,
restoredImage,
this.previousZIndex,
false
);
if (insertSuccess) {
console.log(`恢复图像到z-index位置: ${this.previousZIndex}`);
} else {
// 如果插入失败,回退到普通添加
this.canvas.add(restoredImage);
console.log("z-index恢复失败恢复图像添加到顶层");
}
} else {
// 如果没有保存的z-index添加到顶层
this.canvas.add(restoredImage);
console.log("恢复图像添加到顶层");
}
restoredImage.setCoords();
// 更新引用
this.targetLayer.fabricObject = restoredImage;
this.layerManager.updateLayerObject(this.targetLayer.id, restoredImage);
});
resolve();
} catch (error) {
reject(error);
}
);
});
});
}
async removeCurrentImage() {
await optimizeCanvasRendering(this.canvas, async () => {
if (this.newObjectId) {
const { object: currentObject } = findObjectById(
this.canvas,
this.newObjectId
);
const { object: currentObject } = findObjectById(this.canvas, this.newObjectId);
if (currentObject) {
const removeSuccess = removeCanvasObjectByObject(
this.canvas,
currentObject
);
const removeSuccess = removeCanvasObjectByObject(this.canvas, currentObject);
if (removeSuccess) {
this.targetLayer.fabricObject = null;
this.layerManager.updateLayerObject(this.targetLayer.id, null);
@@ -976,10 +925,7 @@ export class AddImageToLayerCommand extends Command {
this.canvas.remove(this.addedObject);
// 从图层管理器中移除
this.layerManager.removeObjectFromLayer(
this.addedObject.id,
this.layerId
);
this.layerManager.removeObjectFromLayer(this.addedObject.id, this.layerId);
this.isExecuted = false;
@@ -1042,19 +988,14 @@ export class AddImageToLayerCommand extends Command {
this.retryCount = attempt;
if (attempt === this.maxRetries) {
throw new Error(
`图像加载失败,已重试${this.maxRetries}次: ${error.message}`
);
throw new Error(`图像加载失败,已重试${this.maxRetries}次: ${error.message}`);
}
// 指数退避重试
const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
console.warn(
`图像加载重试 ${attempt + 1}/${this.maxRetries}:`,
error.message
);
console.warn(`图像加载重试 ${attempt + 1}/${this.maxRetries}:`, error.message);
}
}
}
@@ -1062,9 +1003,7 @@ export class AddImageToLayerCommand extends Command {
loadImage() {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(
new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)
);
reject(new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`));
}, this.timeoutMs);
fabric.Image.fromURL(
@@ -1318,23 +1257,16 @@ export class MoveLayerToTopCommand extends Command {
this.parentLayer = null; // 父图层
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId);
// 如果parent 有值 或者 layer 上有parentId 则视为子图层的置顶
if (parent?.id) {
// 查找子图层索引
this.layerIndex = parent?.children?.findIndex(
(layer) => layer.id === this.layerId
);
this.layerIndex = parent?.children?.findIndex((layer) => layer.id === this.layerId);
this.parentLayer = parent;
} else {
// 查找图层索引
this.layerIndex = this.layers.value.findIndex(
(layer) => layer.id === this.layerId
);
this.layerIndex = this.layers.value.findIndex((layer) => layer.id === this.layerId);
}
this.layer = layer;
@@ -1416,23 +1348,16 @@ export class MoveLayerToBottomCommand extends Command {
this.parentLayer = null; // 父图层
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId);
// 如果parent 有值 或者 layer 上有parentId 则视为子图层的置顶
if (parent?.id) {
// 查找子图层索引
this.layerIndex = parent?.children?.findIndex(
(layer) => layer.id === this.layerId
);
this.layerIndex = parent?.children?.findIndex((layer) => layer.id === this.layerId);
this.parentLayer = parent;
} else {
// 查找图层索引
this.layerIndex = this.layers.value.findIndex(
(layer) => layer.id === this.layerId
);
this.layerIndex = this.layers.value.findIndex((layer) => layer.id === this.layerId);
}
this.layer = layer;
@@ -1473,9 +1398,7 @@ export class MoveLayerToBottomCommand extends Command {
async undo() {
if (this.originalIndex !== -1 && this.layer) {
// 获取当前位置
const currentIndex = this.layers.value.findIndex(
(layer) => layer.id === this.layerId
);
const currentIndex = this.layers.value.findIndex((layer) => layer.id === this.layerId);
if (currentIndex !== -1) {
// 移除当前位置
@@ -1526,16 +1449,11 @@ export class MoveLayerToBottomCommand extends Command {
if (layer.isBackground && layer.fabricObject) {
// 背景图层
const originalObj = canvasObjects.find(
(o) => o.id === layer.fabricObject.id
);
const originalObj = canvasObjects.find((o) => o.id === layer.fabricObject.id);
if (originalObj) {
this.canvas.add(originalObj);
}
} else if (
Array.isArray(layer.fabricObjects) &&
layer.fabricObjects.length > 0
) {
} else if (Array.isArray(layer.fabricObjects) && layer.fabricObjects.length > 0) {
// 普通图层和固定图层
layer.fabricObjects.forEach((obj) => {
const originalObj = canvasObjects.find((o) => o.id === obj.id);

View File

@@ -1,11 +1,6 @@
import { Command } from "./Command";
import { fabric } from "fabric-with-all";
import {
LayerType,
OperationType,
createLayer,
findLayerRecursively,
} from "../utils/layerHelper";
import { LayerType, OperationType, createLayer, findLayerRecursively } from "../utils/layerHelper";
import {
generateId,
optimizeCanvasRendering,
@@ -39,10 +34,7 @@ export class RasterizeLayerCommand extends Command {
this.layerManager = options.layerManager;
// 查找目标图层
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId);
this.layer = layer;
this.parentLayer = parent;
this.isGroupLayer = this.layer?.children && this.layer.children.length > 0;
@@ -102,9 +94,7 @@ export class RasterizeLayerCommand extends Command {
console.log(`✅ 图层 ${this.layer.name} 栅格化完成`);
this.canvas?.thumbnailManager?.generateLayerThumbnail?.(
this.rasterizedLayerId
);
this.canvas?.thumbnailManager?.generateLayerThumbnail?.(this.rasterizedLayerId);
return this.rasterizedLayerId;
} catch (error) {
console.error("栅格化图层失败:", error);
@@ -350,10 +340,7 @@ export class RasterizeLayerCommand extends Command {
*/
_replaceLayerInStructure() {
// 1.当前如果是子图层,则插入到子图层的位置
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId);
let insertIndex = 0;
// 说明是子图层
@@ -374,11 +361,7 @@ export class RasterizeLayerCommand extends Command {
if (insertIndex !== -1) {
if (parent) {
const pIndex = this.layers.value.findIndex((l) => l.id === parent.id);
this.layers.value[pIndex].children?.splice?.(
insertIndex,
1,
this.rasterizedLayer
);
this.layers.value[pIndex].children?.splice?.(insertIndex, 1, this.rasterizedLayer);
} else {
this.layers.value.splice(insertIndex, 1, this.rasterizedLayer);
}
@@ -396,14 +379,9 @@ export class RasterizeLayerCommand extends Command {
async _getMaskObject() {
// 如果图层有clippingMask获取对应的fabric对象
if (this.layer?.clippingMask) {
const { object: maskObject } = findObjectById(
this.canvas,
this.layer.clippingMask.id
);
const { object: maskObject } = findObjectById(this.canvas, this.layer.clippingMask.id);
if (maskObject) {
console.log(
`📎 找到遮罩对象: ${maskObject.id}, 类型: ${maskObject.type}`
);
console.log(`📎 找到遮罩对象: ${maskObject.id}, 类型: ${maskObject.type}`);
return maskObject;
}
return await restoreFabricObject(this.layer.clippingMask);
@@ -449,10 +427,7 @@ export class ExportLayerToImageCommand extends Command {
this.layerManager = options.layerManager;
// 查找目标图层
const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId);
this.layer = layer;
this.parentLayer = parent;
this.isGroupLayer = this.layer?.children && this.layer.children.length > 0;

View File

@@ -1,8 +1,4 @@
import {
findObjectById,
generateId,
optimizeCanvasRendering,
} from "../utils/helper.js";
import { findObjectById, generateId, optimizeCanvasRendering } from "../utils/helper.js";
import { imageModeHandler } from "../utils/imageHelper.js";
import { LayerType, OperationType } from "../utils/layerHelper.js";
import { Command, CompositeCommand } from "./Command.js";
@@ -58,9 +54,7 @@ export class BatchInitializeRedGreenModeCommand extends Command {
const layers = this.layerManager.layers?.value || [];
const backgroundLayer = layers.find((layer) => layer.isBackground);
const fixedLayer = layers.find((layer) => layer.isFixed);
const normalLayers = layers.filter(
(layer) => !layer.isBackground && !layer.isFixed
);
const normalLayers = layers.filter((layer) => !layer.isBackground && !layer.isFixed);
if (!backgroundLayer || !fixedLayer || normalLayers.length === 0) {
throw new Error("缺少必要的图层结构");
@@ -76,9 +70,7 @@ export class BatchInitializeRedGreenModeCommand extends Command {
}
: null;
this.originalFixedObjects = fixedLayer.fabricObject
? [fixedLayer.fabricObject]
: [];
this.originalFixedObjects = fixedLayer.fabricObject ? [fixedLayer.fabricObject] : [];
this.originalNormalObjects = normalLayer.fabricObjects
? [...normalLayer.fabricObjects]
@@ -118,11 +110,7 @@ export class BatchInitializeRedGreenModeCommand extends Command {
await this._setupClothingImage(clothingImg, fixedLayer);
// 7. 设置红绿图到普通图层,位置和大小与衣服底图一致
await this._setupRedGreenImage(
redGreenImg,
normalLayer,
this.clothingImage
);
await this._setupRedGreenImage(redGreenImg, normalLayer, this.clothingImage);
// 4. 确保背景图层大小和衣服地图大小一致
const backgroundObject = await this._setupBackgroundLayer(
@@ -207,13 +195,9 @@ export class BatchInitializeRedGreenModeCommand extends Command {
async _createAndActivateEmptyLayer() {
// 创建新的空白图层
const newLayerName = "绘制图层";
const newLayerId = await this.layerManager.createLayer(
newLayerName,
LayerType.BITMAP,
{
undoable: false,
}
);
const newLayerId = await this.layerManager.createLayer(newLayerName, LayerType.BITMAP, {
undoable: false,
});
// 设置为活动图层
if (newLayerId) {
@@ -228,19 +212,14 @@ export class BatchInitializeRedGreenModeCommand extends Command {
await optimizeCanvasRendering(this.canvas, async () => {
// 1. 恢复画布背景
if (this.originalCanvasBackground !== null) {
this.canvas.setBackgroundColor(
this.originalCanvasBackground,
() => {}
);
this.canvas.setBackgroundColor(this.originalCanvasBackground, () => {});
}
// 2. 恢复图层对象
const layers = this.layerManager.layers?.value || [];
const backgroundLayer = layers.find((layer) => layer.isBackground);
const fixedLayer = layers.find((layer) => layer.isFixed);
const normalLayers = layers.filter(
(layer) => !layer.isBackground && !layer.isFixed
);
const normalLayers = layers.filter((layer) => !layer.isBackground && !layer.isFixed);
// 移除当前添加的对象
if (this.clothingImage) {
@@ -252,9 +231,7 @@ export class BatchInitializeRedGreenModeCommand extends Command {
// 移除新创建的空白图层
if (this.newEmptyLayerId) {
const emptyLayerIndex = layers.findIndex(
(layer) => layer.id === this.newEmptyLayerId
);
const emptyLayerIndex = layers.findIndex((layer) => layer.id === this.newEmptyLayerId);
if (emptyLayerIndex !== -1) {
layers.splice(emptyLayerIndex, 1);
}
@@ -270,9 +247,7 @@ export class BatchInitializeRedGreenModeCommand extends Command {
// 恢复固定图层
if (fixedLayer) {
fixedLayer.fabricObject =
this.originalFixedObjects.length > 0
? this.originalFixedObjects[0]
: null;
this.originalFixedObjects.length > 0 ? this.originalFixedObjects[0] : null;
if (fixedLayer.fabricObject) {
this.canvas.add(fixedLayer.fabricObject);
@@ -311,8 +286,7 @@ export class BatchInitializeRedGreenModeCommand extends Command {
// 4. 恢复工具状态
if (this.toolManager && this.originalToolState) {
this.toolManager.isRedGreenMode =
this.originalToolState.isRedGreenMode;
this.toolManager.isRedGreenMode = this.originalToolState.isRedGreenMode;
if (this.originalToolState.currentTool) {
this.toolManager.setTool(this.originalToolState.currentTool);
}
@@ -434,10 +408,7 @@ export class BatchInitializeRedGreenModeCommand extends Command {
// 清除固定图层原有内容
if (fixedLayer.fabricObject) {
const { object } = findObjectById(
this.canvas,
fixedLayer.fabricObject.id
);
const { object } = findObjectById(this.canvas, fixedLayer.fabricObject.id);
if (object) this.canvas.remove(object);
}

View File

@@ -110,9 +110,7 @@ export class AddToSelectionCommand extends Command {
}
// 添加到选区
const result = await this.selectionManager.addToSelection(
this.newSelection
);
const result = await this.selectionManager.addToSelection(this.newSelection);
return result;
}
@@ -155,9 +153,7 @@ export class RemoveFromSelectionCommand extends Command {
}
// 从选区中移除
const result = await this.selectionManager.removeFromSelection(
this.removeSelection
);
const result = await this.selectionManager.removeFromSelection(this.removeSelection);
return result;
}
@@ -203,9 +199,7 @@ export class FeatherSelectionCommand extends Command {
}
// 应用羽化
const result = await this.selectionManager.featherSelection(
this.featherAmount
);
const result = await this.selectionManager.featherSelection(this.featherAmount);
return result;
}
@@ -347,8 +341,7 @@ export class CopySelectionToNewLayerCommand extends CompositeCommand {
}
// 确定源图层
const sourceId =
this.sourceLayerId || this.layerManager.getActiveLayerId();
const sourceId = this.sourceLayerId || this.layerManager.getActiveLayerId();
const sourceLayer = this.layerManager.getLayerById(sourceId);
if (!sourceLayer || !sourceLayer.fabricObjects) {
console.error("无法复制选区:源图层无效或为空");
@@ -356,10 +349,7 @@ export class CopySelectionToNewLayerCommand extends CompositeCommand {
}
// 创建新图层
this.newLayerId = await this.layerManager.createLayer(
this.newLayerName,
LayerType.EMPTY
);
this.newLayerId = await this.layerManager.createLayer(this.newLayerName, LayerType.EMPTY);
// 获取选区内的对象
const objectsToCopy = sourceLayer.fabricObjects.filter((obj) => {

View File

@@ -401,9 +401,7 @@ export class CreateTextCommand extends Command {
// 更新图层的对象列表
if (layer) {
layer.fabricObjects = layer.fabricObjects || [];
layer.fabricObjects.push(
this.textObject.toObject(["id", "layerId", "layerName"])
);
layer.fabricObjects.push(this.textObject.toObject(["id", "layerId", "layerName"]));
}
// 现在可以安全地设置为活动图层
@@ -416,9 +414,7 @@ export class CreateTextCommand extends Command {
this.layerManager?.toolManager?.setTool?.(OperationType.SELECT);
});
console.log(
`✅ 文本对象已创建: "${finalOptions.text}",位置: (${this.x}, ${this.y})`
);
console.log(`✅ 文本对象已创建: "${finalOptions.text}",位置: (${this.x}, ${this.y})`);
return this.textObject;
} catch (error) {
console.error("创建文本对象失败:", error);
@@ -467,15 +463,11 @@ export class CreateTextCommand extends Command {
parentLayer.children = parentLayer.children || [];
parentLayer.children.splice(insertIndex, 0, newLayer);
console.log(
`新图层已插入到子图层位置: ${insertIndex} (父图层: ${parentLayer.name})`
);
console.log(`新图层已插入到子图层位置: ${insertIndex} (父图层: ${parentLayer.name})`);
} else {
// 当前激活图层是一级图层
// 在一级图层中,插入到激活图层之上
const activeLayerIndex = layers.findIndex(
(layer) => layer.id === currentActiveLayerId
);
const activeLayerIndex = layers.findIndex((layer) => layer.id === currentActiveLayerId);
insertIndex = Math.max(0, activeLayerIndex);
layers.splice(insertIndex, 0, newLayer);
@@ -551,9 +543,7 @@ export class CreateTextCommand extends Command {
// 恢复原活动图层
if (this.oldActiveLayerId && this.layerManager) {
// 检查原活动图层是否还存在
const originalLayer = this.layerManager.getLayerById(
this.oldActiveLayerId
);
const originalLayer = this.layerManager.getLayerById(this.oldActiveLayerId);
if (originalLayer) {
this.layerManager.setActiveLayer(this.oldActiveLayerId);
} else {
@@ -603,9 +593,7 @@ export class CreateTextCommand extends Command {
// 从子图层中移除
if (positionInfo.parent.children && positionInfo.index >= 0) {
positionInfo.parent.children.splice(positionInfo.index, 1);
console.log(
`已从子图层移除: ${this.layerId} (父图层: ${positionInfo.parent.name})`
);
console.log(`已从子图层移除: ${this.layerId} (父图层: ${positionInfo.parent.name})`);
}
} else {
// 从一级图层中移除

View File

@@ -0,0 +1,244 @@
import { findLayerRecursively } from "../utils/layerHelper";
import { restoreFabricObject } from "../utils/objectHelper";
import { Command } from "./Command";
/**
* 更新组图层遮罩位置命令
* 当组图层的对象移动时,同步更新遮罩的位置
*/
export class UpdateGroupMaskPositionCommand extends Command {
constructor(options) {
super({
name: "更新组图层遮罩位置",
saveState: true,
});
this.canvas = options.canvas;
this.layers = options.layers;
this.layerManager = options.layerManager;
this.layerId = options.layerId;
this.deltaX = options.deltaX || 0;
this.deltaY = options.deltaY || 0;
this.maskInitialLeft = options.maskInitialLeft || 0;
this.maskInitialTop = options.maskInitialTop || 0;
this.isExecuteRealtime = options.isExecuteRealtime || false;
this.activeSelection = this.canvas.getActiveObject() || {};
this.isFristExecute = true;
// 查找目标图层
this.layer = findLayerRecursively(this.layers.value, this.layerId)?.layer;
if (!this.layer || !this.layer.clippingMask) {
console.warn(`图层 ${this.layerId} 不存在或没有遮罩`);
return false;
}
// 保存原始遮罩位置(用于撤销)
// 保存原始位置
this.originalMaskPosition = {
left: this.maskInitialLeft || 0,
top: this.maskInitialTop || 0,
};
// 收集当前选择的所有对象位置
this.originalObjectsPostion = this.activeSelection.getObjects().map((obj) => {
return {
left: obj.left || 0,
top: obj.top || 0,
id: obj.id,
};
});
this.originalSelectionPosition = {
left: this.activeSelection.left || 0,
top: this.activeSelection.top || 0,
};
console.log(
`🛠️ 初始化更新组图层遮罩位置命令: ${this.name}, 图层ID: ${this.layerId}, 初始位置: (${this.originalMaskPosition.left}, ${this.originalMaskPosition.top})`
);
this.newMaskPosition = null;
}
async execute() {
try {
// 计算新位置
const newLeft = this.maskInitialLeft + this.deltaX;
const newTop = this.maskInitialTop + this.deltaY;
// 更新遮罩位置
this.layer.clippingMask.left = newLeft;
this.layer.clippingMask.top = newTop;
this.newMaskPosition = {
left: newLeft,
top: newTop,
};
// 更新所有使用此遮罩的子图层对象
await this._updateChildObjectsClipPath(this.layer, false, true);
this.isFristExecute = false;
console.log(`✅ 组图层遮罩位置已更新: (${newLeft}, ${newTop})`);
return true;
} catch (error) {
console.error("更新组图层遮罩位置失败:", error);
return false;
}
}
async undo() {
try {
if (!this.originalMaskPosition) {
console.warn("没有原始遮罩位置数据,无法撤销");
return false;
}
// 查找目标图层
const layer = this.layers.value.find((l) => l.id === this.layerId);
if (!layer || !layer.clippingMask) {
console.warn(`图层 ${this.layerId} 不存在或没有遮罩`);
return false;
}
console.log(
`↶ 撤销更新组图层遮罩位置: ${this.originalMaskPosition.left}, 图层ID: ${this.originalMaskPosition.top}`
);
// 恢复原始位置
layer.clippingMask.left = this.originalMaskPosition.left;
layer.clippingMask.top = this.originalMaskPosition.top;
// 更新所有使用此遮罩的子图层对象
await this._updateChildObjectsClipPath(layer, true);
// await this.layerManager.updateLayersObjectsInteractivity();
// this.canvas.renderAll();
console.log(
`↶ 组图层遮罩位置已恢复: (${this.originalMaskPosition.left}, ${this.originalMaskPosition.top})`
);
return true;
} catch (error) {
console.error("撤销组图层遮罩位置更新失败:", error);
return false;
}
}
/**
* 实时执行(不记录到历史记录)
* 用于拖拽过程中的实时更新
*/
executeRealtime() {
try {
// 查找目标图层
const layer = this.layers.value.find((l) => l.id === this.layerId);
if (!layer || !layer.clippingMask) {
return false;
}
// 计算新位置
const newLeft = this.maskInitialLeft + this.deltaX;
const newTop = this.maskInitialTop + this.deltaY;
// 更新遮罩位置
layer.clippingMask.left = newLeft;
layer.clippingMask.top = newTop;
// 更新所有使用此遮罩的子图层对象(不需要等待)
this._updateChildObjectsClipPath(layer);
return true;
} catch (error) {
console.error("实时更新组图层遮罩位置失败:", error);
return false;
}
}
/**
* 更新子图层对象的裁剪路径
* @param {Object} layer 组图层对象
* @private
*/
async _updateChildObjectsClipPath(layer, isUndo = false, isExecute = false) {
if (!layer.children || layer.children.length === 0) {
return;
}
try {
// 重新创建遮罩对象
const clippingMaskFabricObject = await restoreFabricObject(layer.clippingMask, this.canvas);
if (!clippingMaskFabricObject) {
console.warn("无法恢复遮罩对象");
return;
}
clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({
absolutePositioned: true,
});
clippingMaskFabricObject.dirty = true;
clippingMaskFabricObject.setCoords();
// 更新所有子图层对象的裁剪路径
layer.children.forEach((childLayer) => {
// 更新 fabricObjects 中的对象
childLayer.fabricObjects?.forEach((obj) => {
const fabricObject = this.canvas.getObjects().find((o) => o.id === obj.id);
if (fabricObject) {
fabricObject.clipPath = clippingMaskFabricObject;
fabricObject.dirty = true;
fabricObject.setCoords();
}
});
// 更新单个 fabricObject
if (childLayer.fabricObject) {
const fabricObject = this.canvas
.getObjects()
.find((o) => o.id === childLayer.fabricObject.id);
if (fabricObject) {
fabricObject.clipPath = clippingMaskFabricObject;
fabricObject.dirty = true;
fabricObject.setCoords();
}
}
});
if (isUndo) {
this.activeSelection.set({
left: this.originalSelectionPosition.left - this.deltaX,
top: this.originalSelectionPosition.top - this.deltaY,
});
this.activeSelection.dirty = true;
this.activeSelection.setCoords();
}
if (isExecute && !this.isFristExecute) {
this.activeSelection.set({
left: this.activeSelection.left + this.deltaX,
top: this.activeSelection.top + this.deltaY,
});
this.activeSelection.dirty = true;
this.activeSelection.setCoords();
}
// 触发画布重新渲染
this.canvas.renderAll();
} catch (error) {
console.error("更新子图层对象裁剪路径失败:", error);
}
}
getInfo() {
return {
name: this.name,
layerId: this.layerId,
deltaX: this.deltaX,
deltaY: this.deltaY,
originalPosition: this.originalMaskPosition,
newPosition: this.newMaskPosition,
};
}
}