feat : 显示选区逻辑完成

This commit is contained in:
bighuixiang
2025-07-21 01:17:25 +08:00
parent ba04930966
commit c756d7377f
9 changed files with 983 additions and 308 deletions

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,10 @@
import { Command } from "./Command"; 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 { createStaticCanvas } from "../utils/canvasFactory";
import { AddObjectToLayerCommand } from "./ObjectLayerCommands"; import { AddObjectToLayerCommand } from "./ObjectLayerCommands";
import { ToolCommand } from "./ToolCommands"; import { ToolCommand } from "./ToolCommands";
@@ -110,11 +115,15 @@ export class AddLayerCommand extends Command {
parentLayer.children = parentLayer.children || []; parentLayer.children = parentLayer.children || [];
parentLayer.children.splice(insertIndex, 0, newLayer); parentLayer.children.splice(insertIndex, 0, newLayer);
console.log(`新图层已插入到子图层位置: ${insertIndex} (父图层: ${parentLayer.name})`); console.log(
`新图层已插入到子图层位置: ${insertIndex} (父图层: ${parentLayer.name})`
);
} else { } else {
// 当前激活图层是一级图层 // 当前激活图层是一级图层
// 在一级图层中,插入到激活图层之上 // 在一级图层中,插入到激活图层之上
const activeLayerIndex = layers.findIndex((layer) => layer.id === currentActiveLayerId); const activeLayerIndex = layers.findIndex(
(layer) => layer.id === currentActiveLayerId
);
insertIndex = Math.max(0, activeLayerIndex); insertIndex = Math.max(0, activeLayerIndex);
layers.splice(insertIndex, 0, newLayer); layers.splice(insertIndex, 0, newLayer);
@@ -215,11 +224,18 @@ export class PasteLayerCommand extends Command {
if (this.isGroupLayer) { if (this.isGroupLayer) {
// 粘贴为新的组图层递归生成子图层ID // 粘贴为新的组图层递归生成子图层ID
const { groupLayer, allObjects } = await this._createGroupLayerFromClipboard(data); const { groupLayer, allObjects } =
await this._createGroupLayerFromClipboard(data);
if (groupLayer?.clippingMask) { if (groupLayer?.clippingMask) {
// 给遮罩蒙层也添加上偏移 // 给遮罩蒙层也添加上偏移
groupLayer.clippingMask.left += 30; groupLayer.clippingMask.left += 30;
groupLayer.clippingMask.top += 30; groupLayer.clippingMask.top += 30;
if (groupLayer.selectObject) {
// 如果有选区 则选区位置也要更新
groupLayer.selectObject.left = groupLayer.clippingMask.left;
groupLayer.selectObject.top = groupLayer.clippingMask.top;
}
} }
this.newLayer = groupLayer; this.newLayer = groupLayer;
this.newLayer.id = this.newLayerId; this.newLayer.id = this.newLayerId;
@@ -232,7 +248,8 @@ export class PasteLayerCommand extends Command {
} }
// 重新创建遮罩对象 // 重新创建遮罩对象
const clippingMaskFabricObject = const clippingMaskFabricObject =
(await restoreFabricObject(groupLayer?.clippingMask, this.canvas)) || null; (await restoreFabricObject(groupLayer?.clippingMask, this.canvas)) ||
null;
clippingMaskFabricObject.clipPath = null; clippingMaskFabricObject.clipPath = null;
clippingMaskFabricObject.set({ clippingMaskFabricObject.set({
@@ -411,7 +428,9 @@ export class PasteLayerCommand extends Command {
if (obj.top !== undefined) obj.top += offset; if (obj.top !== undefined) obj.top += offset;
} }
this.canvas.add(obj); this.canvas.add(obj);
this.newLayer.fabricObjects.push(obj.toObject(["id", "layerId", "layerName"])); this.newLayer.fabricObjects.push(
obj.toObject(["id", "layerId", "layerName"])
);
this.createdObjects.push(obj); this.createdObjects.push(obj);
}); });
this._onObjectsRestored(data); this._onObjectsRestored(data);
@@ -436,7 +455,10 @@ export class PasteLayerCommand extends Command {
async undo() { async undo() {
if (!this.newLayer || !this.newLayerId) return; if (!this.newLayer || !this.newLayerId) return;
const { layer, parent } = findLayerRecursively(this.layers.value, this.newLayerId); const { layer, parent } = findLayerRecursively(
this.layers.value,
this.newLayerId
);
if (!layer) { if (!layer) {
console.error(`图层 ${this.newLayerId} 不存在, 无法撤销`); console.error(`图层 ${this.newLayerId} 不存在, 无法撤销`);
return false; return false;
@@ -444,13 +466,17 @@ export class PasteLayerCommand extends Command {
if (parent) { if (parent) {
// 如果是子图层,直接从父图层中删除 // 如果是子图层,直接从父图层中删除
const index = parent.children.findIndex((child) => child.id === this.newLayerId); const index = parent.children.findIndex(
(child) => child.id === this.newLayerId
);
if (index !== -1) { if (index !== -1) {
parent.children.splice(index, 1); parent.children.splice(index, 1);
} }
} else { } 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) { if (index !== -1) {
this.layers.value.splice(index, 1); this.layers.value.splice(index, 1);
} }
@@ -496,9 +522,12 @@ export class RemoveLayerCommand extends Command {
this.layers = options.layers; this.layers = options.layers;
this.layerId = options.layerId; this.layerId = options.layerId;
this.activeLayerId = options.activeLayerId; this.activeLayerId = options.activeLayerId;
this.layerManager = options.layerManager || null;
// 查找要删除的图层 // 查找要删除的图层
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.removedLayer = this.layers.value[this.layerIndex];
this.isActiveLayer = this.layerId === this.activeLayerId.value; this.isActiveLayer = this.layerId === this.activeLayerId.value;
@@ -546,7 +575,7 @@ export class RemoveLayerCommand extends Command {
return allObjects; return allObjects;
} }
execute() { async execute() {
if (this.layerIndex === -1 || !this.removedLayer) { if (this.layerIndex === -1 || !this.removedLayer) {
console.error(`图层 ${this.layerId} 不存在`); console.error(`图层 ${this.layerId} 不存在`);
return false; return false;
@@ -565,7 +594,9 @@ export class RemoveLayerCommand extends Command {
// 如果删除的是当前活动图层,需要更新活动图层 // 如果删除的是当前活动图层,需要更新活动图层
if (this.isActiveLayer) { if (this.isActiveLayer) {
// 查找最近的非背景层作为新的活动图层 // 查找最近的非背景层作为新的活动图层
const newActiveLayer = this.layers.value.find((layer) => !layer.isBackground); const newActiveLayer = this.layers.value.find(
(layer) => !layer.isBackground
);
if (newActiveLayer) { if (newActiveLayer) {
this.activeLayerId.value = newActiveLayer.id; this.activeLayerId.value = newActiveLayer.id;
} else { } else {
@@ -574,9 +605,11 @@ export class RemoveLayerCommand extends Command {
} }
// 重新渲染画布 // 重新渲染画布
if (this.canvas) { // if (this.canvas) {
this.canvas.renderAll(); // this.canvas.renderAll();
} // }
await this.layerManager?.updateLayersObjectsInteractivity?.();
// this.canvas.renderAll();
console.log( console.log(
`✅ 已移除图层: ${this.removedLayer.name} (ID: ${this.layerId}),包含 ${this.originalObjects.length} 个对象` `✅ 已移除图层: ${this.removedLayer.name} (ID: ${this.layerId}),包含 ${this.originalObjects.length} 个对象`
@@ -584,13 +617,14 @@ export class RemoveLayerCommand extends Command {
return true; return true;
} }
undo() { async undo() {
// 恢复图层到原位置 // 恢复图层到原位置
if (this.layerIndex !== -1 && this.removedLayer) { if (this.layerIndex !== -1 && this.removedLayer) {
this.layers.value.splice(this.layerIndex, 0, this.removedLayer); this.layers.value.splice(this.layerIndex, 0, this.removedLayer);
debugger;
// 使用优化渲染批处理恢复真实对象到画布 // 使用优化渲染批处理恢复真实对象到画布
optimizeCanvasRendering(this.canvas, () => { await optimizeCanvasRendering(this.canvas, () => {
// 倒序添加对象,确保下标越小的子图层在画布中越靠前 // 倒序添加对象,确保下标越小的子图层在画布中越靠前
this.originalObjects this.originalObjects
.slice() .slice()
@@ -605,6 +639,9 @@ export class RemoveLayerCommand extends Command {
}); });
}); });
await this.layerManager?.updateLayersObjectsInteractivity?.();
this.canvas.renderAll();
// 如果删除的是当前活动图层,恢复活动图层 // 如果删除的是当前活动图层,恢复活动图层
if (this.isActiveLayer) { if (this.isActiveLayer) {
this.activeLayerId.value = this.layerId; this.activeLayerId.value = this.layerId;
@@ -643,22 +680,30 @@ export class MoveLayerCommand extends Command {
this.parentLayer = null; // 父图层 this.parentLayer = null; // 父图层
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId); const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
// 如果parent 有值 或者 layer 上有parentId 则视为子图层的置顶 // 如果parent 有值 或者 layer 上有parentId 则视为子图层的置顶
if (parent?.id) { 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; this.parentLayer = parent;
} else { } 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.layer = layer;
this.originalIndex = this.layerIndex; 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() { async execute() {
@@ -749,7 +794,9 @@ export class ToggleLayerVisibilityCommand extends Command {
// 更新画布上图层对象的可见性 // 更新画布上图层对象的可见性
if (this.canvas) { 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) => { layerObjects.forEach((obj) => {
obj.visible = this.layer.visible; obj.visible = this.layer.visible;
}); });
@@ -790,7 +837,10 @@ export class ToggleChildLayerVisibilityCommand extends Command {
this.layerManager = options.layerManager; 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.parentLayer = parent;
this.childLayer = layer; this.childLayer = layer;
@@ -807,7 +857,9 @@ export class ToggleChildLayerVisibilityCommand extends Command {
// 更新画布上图层对象的可见性 // 更新画布上图层对象的可见性
if (this.canvas) { 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) => { layerObjects.forEach((obj) => {
obj.visible = this.childLayer.visible; obj.visible = this.childLayer.visible;
@@ -866,7 +918,9 @@ export class RenameLayerCommand extends Command {
// 更新图层对象上的图层名称 // 更新图层对象上的图层名称
if (this.canvas) { 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) => { layerObjects.forEach((obj) => {
obj.layerName = this.newName; obj.layerName = this.newName;
@@ -883,7 +937,9 @@ export class RenameLayerCommand extends Command {
// 恢复图层对象上的图层名称 // 恢复图层对象上的图层名称
if (this.canvas) { 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) => { layerObjects.forEach((obj) => {
obj.layerName = this.oldName; obj.layerName = this.oldName;
@@ -918,7 +974,10 @@ 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.layer = layer;
this.parentLayer = parent || null; // 父图层 this.parentLayer = parent || null; // 父图层
this.oldLocked = this.layer ? this.layer.locked : null; this.oldLocked = this.layer ? this.layer.locked : null;
@@ -936,7 +995,11 @@ export class LayerLockCommand extends Command {
layer.locked = locked; 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) => { layer.children.forEach((child) => {
this._updateLayerLockState(child, locked); this._updateLayerLockState(child, locked);
}); });
@@ -958,7 +1021,11 @@ export class LayerLockCommand extends Command {
// 更新画布上对象的可选择状态 // 更新画布上对象的可选择状态
await this.layerManager?.updateLayersObjectsInteractivity(); await this.layerManager?.updateLayersObjectsInteractivity();
console.log(`${newLocked ? "锁定" : "解锁"}图层: ${this.layer.name} (ID: ${this.layerId})`); console.log(
`${newLocked ? "锁定" : "解锁"}图层: ${this.layer.name} (ID: ${
this.layerId
})`
);
return true; return true;
} }
@@ -970,7 +1037,9 @@ export class LayerLockCommand extends Command {
// 更新画布上对象的可选择状态 // 更新画布上对象的可选择状态
await this.layerManager?.updateLayersObjectsInteractivity(); await this.layerManager?.updateLayersObjectsInteractivity();
console.log(`恢复图层锁定状态: ${this.layer.name} (锁定: ${this.oldLocked})`); console.log(
`恢复图层锁定状态: ${this.layer.name} (锁定: ${this.oldLocked})`
);
} }
} }
@@ -1018,7 +1087,9 @@ export class SetLayerOpacityCommand extends Command {
// 更新画布上对象的不透明度 // 更新画布上对象的不透明度
if (this.canvas) { 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) => { layerObjects.forEach((obj) => {
obj.opacity = this.opacity; obj.opacity = this.opacity;
@@ -1037,7 +1108,9 @@ export class SetLayerOpacityCommand extends Command {
// 更新画布上对象的不透明度 // 更新画布上对象的不透明度
if (this.canvas) { 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) => { layerObjects.forEach((obj) => {
obj.opacity = this.oldOpacity; obj.opacity = this.oldOpacity;
@@ -1091,7 +1164,9 @@ export class SetLayerBlendModeCommand extends Command {
// 更新画布上对象的混合模式 // 更新画布上对象的混合模式
if (this.canvas) { 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) => { layerObjects.forEach((obj) => {
obj.globalCompositeOperation = this.blendMode; obj.globalCompositeOperation = this.blendMode;
@@ -1110,7 +1185,9 @@ export class SetLayerBlendModeCommand extends Command {
// 更新画布上对象的混合模式 // 更新画布上对象的混合模式
if (this.canvas) { 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) => { layerObjects.forEach((obj) => {
obj.globalCompositeOperation = this.oldBlendMode; obj.globalCompositeOperation = this.oldBlendMode;
@@ -1150,11 +1227,17 @@ export class MergeLayersCommand extends Command {
// 备份原图层 // 备份原图层
this.originalLayers = [...this.layers.value]; this.originalLayers = [...this.layers.value];
// 新图层ID // 新图层ID
this.newLayerId = `merged_layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; this.newLayerId = `merged_layer_${Date.now()}_${Math.floor(
Math.random() * 1000
)}`;
} }
execute() { 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("合并图层至少需要两个图层"); console.error("合并图层至少需要两个图层");
return null; return null;
} }
@@ -1215,7 +1298,9 @@ 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); this.layers.value.splice(topLayerIndex, 0, mergedLayer);
@@ -1276,13 +1361,18 @@ export class GroupLayersCommand extends Command {
this.originalLayers = [...this.layers.value]; this.originalLayers = [...this.layers.value];
// 新组ID // 新组ID
this.groupId = 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 this.originalActiveLayerId = this.activeLayerId.value; // 备份原活动图层ID
} }
async execute() { 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("组合图层至少需要两个图层"); console.error("组合图层至少需要两个图层");
return null; return null;
} }
@@ -1326,7 +1416,9 @@ 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); this.layers.value.splice(topLayerIndex, 0, groupLayer);
@@ -1380,7 +1472,9 @@ export class UngroupLayersCommand extends Command {
async execute() { 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) { if (groupIndex === -1) {
console.error(`找不到组图层 ${this.groupId}`); console.error(`找不到组图层 ${this.groupId}`);
@@ -1451,14 +1545,18 @@ export class MergeLayerObjectsCommand extends Command {
// 备份原始对象,用于撤销 // 备份原始对象,用于撤销
if (this.activeLayer && Array.isArray(this.activeLayer.fabricObjects)) { if (this.activeLayer && Array.isArray(this.activeLayer.fabricObjects)) {
this.originalObjects = this.originalObjects =
this.canvas?.getObjects()?.filter((fItem) => fItem.layerId === this.activeLayer.id) || []; this.canvas
?.getObjects()
?.filter((fItem) => fItem.layerId === this.activeLayer.id) || [];
} else { } else {
this.originalObjects = []; this.originalObjects = [];
} }
// 新合并图像对象 // 新合并图像对象
this.mergedImage = null; 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() { async execute() {
@@ -1494,7 +1592,10 @@ export class MergeLayerObjectsCommand extends Command {
// 异步处理图像合并 // 异步处理图像合并
try { try {
const mergedImage = await this._createMergedImageAsync(objectsToMerge, bounds); const mergedImage = await this._createMergedImageAsync(
objectsToMerge,
bounds
);
this._setupMergedImage(mergedImage, bounds); this._setupMergedImage(mergedImage, bounds);
this._replaceObjects(mergedImage); this._replaceObjects(mergedImage);
@@ -1644,7 +1745,9 @@ export class MergeLayerObjectsCommand extends Command {
}); });
tempCanvas.add(clonedObj); tempCanvas.add(clonedObj);
console.log(`添加对象 ${index + 1}/${objects.length}: ${obj.type || "unknown"}`); console.log(
`添加对象 ${index + 1}/${objects.length}: ${obj.type || "unknown"}`
);
} catch (error) { } catch (error) {
console.error(`添加对象到临时画布时发生错误:`, error); console.error(`添加对象到临时画布时发生错误:`, error);
} }
@@ -1703,7 +1806,10 @@ 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); this.canvas.remove(this.fabricImage);
} }
@@ -1719,7 +1825,9 @@ export class MergeLayerObjectsCommand extends Command {
// 更新缩略图 // 更新缩略图
if (this.canvas.thumbnailManager) { if (this.canvas.thumbnailManager) {
setTimeout(() => { setTimeout(() => {
this.canvas.thumbnailManager.generateLayerThumbnail(this.activeLayer.id); this.canvas.thumbnailManager.generateLayerThumbnail(
this.activeLayer.id
);
}, 100); }, 100);
} }
} }
@@ -1763,7 +1871,9 @@ export class MergeLayerObjectsCommand extends Command {
// 更新缩略图 // 更新缩略图
if (this.canvas.thumbnailManager) { if (this.canvas.thumbnailManager) {
setTimeout(() => { setTimeout(() => {
this.canvas.thumbnailManager.generateLayerThumbnail(this.activeLayer.id); this.canvas.thumbnailManager.generateLayerThumbnail(
this.activeLayer.id
);
}, 100); }, 100);
} }
} }
@@ -1851,7 +1961,8 @@ export class LayerObjectsToGroupCommand extends Command {
this.existingGroupId = null; this.existingGroupId = null;
this.groupObjectId = null; this.groupObjectId = null;
this.newGroupId = 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; this.wasGroupCreated = false;
// 保存原始位置信息 // 保存原始位置信息
@@ -1912,7 +2023,10 @@ export class LayerObjectsToGroupCommand extends Command {
const newObjectsToAdd = []; const newObjectsToAdd = [];
if (this.fabricImage) { 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); const savedPosition = this.newObjectPositions.get(this.fabricImage.id);
this.fabricImage.set({ this.fabricImage.set({
left: savedPosition.left, left: savedPosition.left,
@@ -1925,7 +2039,9 @@ export class LayerObjectsToGroupCommand extends Command {
flipX: savedPosition.flipX, flipX: savedPosition.flipX,
flipY: savedPosition.flipY, flipY: savedPosition.flipY,
}); });
console.log(`🔄 重做时恢复新对象位置: (${savedPosition.left}, ${savedPosition.top})`); console.log(
`🔄 重做时恢复新对象位置: (${savedPosition.left}, ${savedPosition.top})`
);
} }
newObjectsToAdd.push(this.fabricImage); newObjectsToAdd.push(this.fabricImage);
@@ -1978,7 +2094,9 @@ export class LayerObjectsToGroupCommand extends Command {
if (!this.existingGroupId) { if (!this.existingGroupId) {
const groupObjects = 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; this.existingGroupId = groupObjects?.id || null;
} }
@@ -2078,7 +2196,9 @@ export class LayerObjectsToGroupCommand extends Command {
// 更新图层的对象列表 // 更新图层的对象列表
// this.activeLayer.fabricObjects = [groupObject]; // this.activeLayer.fabricObjects = [groupObject];
this.activeLayer.fabricObjects = [groupObject.toObject(["id", "layerId", "layerName"])]; this.activeLayer.fabricObjects = [
groupObject.toObject(["id", "layerId", "layerName"]),
];
} }
/** /**
@@ -2164,7 +2284,10 @@ export class LayerObjectsToGroupCommand extends Command {
restorePosition = { ...originalPosition }; restorePosition = { ...originalPosition };
} else { } else {
// 计算对象在画布中的当前绝对位置 // 计算对象在画布中的当前绝对位置
restorePosition = this._calculateObjectAbsolutePosition(obj, groupObject); restorePosition = this._calculateObjectAbsolutePosition(
obj,
groupObject
);
} }
objectsToRestore.push({ objectsToRestore.push({
@@ -2219,7 +2342,9 @@ export class LayerObjectsToGroupCommand extends Command {
restoredObjects.push(obj); restoredObjects.push(obj);
console.log( console.log(
`✅ 恢复原始对象 ${obj.id || obj.type} 到位置 (${position.left}, ${position.top})` `✅ 恢复原始对象 ${obj.id || obj.type} 到位置 (${position.left}, ${
position.top
})`
); );
} catch (error) { } catch (error) {
console.error(`恢复对象 ${obj.id || obj.type} 时发生错误:`, error); console.error(`恢复对象 ${obj.id || obj.type} 时发生错误:`, error);
@@ -2279,7 +2404,10 @@ export class LayerObjectsToGroupCommand extends Command {
const objectPoint = new fabric.Point(obj.left || 0, obj.top || 0); 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); const totalScaleX = (group.scaleX || 1) * (obj.scaleX || 1);
@@ -2447,7 +2575,8 @@ export class CreateImageLayerCommand extends Command {
// 存储执行过程中的结果 // 存储执行过程中的结果
this.newLayerId = 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.commands = [];
this.executedCommands = []; this.executedCommands = [];
} }
@@ -2462,7 +2591,8 @@ export class CreateImageLayerCommand extends Command {
this.executedCommands = []; this.executedCommands = [];
// 生成图层名称 // 生成图层名称
const fileName = this.layerName || `图片 ${new Date().toLocaleTimeString()}`; const fileName =
this.layerName || `图片 ${new Date().toLocaleTimeString()}`;
this.fabricImage.set({ this.fabricImage.set({
id: this.imageId, id: this.imageId,
@@ -2533,7 +2663,9 @@ export class CreateImageLayerCommand extends Command {
return true; return true;
} }
console.log(`↩️ 开始撤销创建图片图层操作,共 ${this.executedCommands.length} 个子命令`); console.log(
`↩️ 开始撤销创建图片图层操作,共 ${this.executedCommands.length} 个子命令`
);
try { try {
// 逆序撤销已执行的命令 // 逆序撤销已执行的命令
@@ -2549,7 +2681,10 @@ export class CreateImageLayerCommand extends Command {
} }
console.log(`✅ 子命令撤销成功: ${command.constructor.name}`); console.log(`✅ 子命令撤销成功: ${command.constructor.name}`);
} catch (error) { } catch (error) {
console.error(`❌ 子命令撤销失败: ${command.constructor.name}`, error); console.error(
`❌ 子命令撤销失败: ${command.constructor.name}`,
error
);
} }
} }
} }
@@ -2582,7 +2717,10 @@ export class CreateImageLayerCommand extends Command {
} }
console.log(`✅ 子命令回滚成功: ${command.constructor.name}`); console.log(`✅ 子命令回滚成功: ${command.constructor.name}`);
} catch (error) { } catch (error) {
console.error(`❌ 子命令回滚失败: ${command.constructor.name}`, error); console.error(
`❌ 子命令回滚失败: ${command.constructor.name}`,
error
);
} }
} }
} }
@@ -2717,7 +2855,9 @@ export class ReorderChildLayersCommand extends Command {
async execute() { 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; if (!parentLayer) return false;
// 获取子图层 // 获取子图层
@@ -2867,11 +3007,15 @@ export class CutLayerCommand extends Command {
// 生成新图层ID和名称 // 生成新图层ID和名称
this.newLayerId = this.newLayerId =
generateId("layer_") || `layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`; generateId("layer_") ||
`layer_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
} }
async execute() { async execute() {
const { layer, parent } = findLayerRecursively(this.layers.value, this.layerId); const { layer, parent } = findLayerRecursively(
this.layers.value,
this.layerId
);
const sourceLayer = layer; const sourceLayer = layer;
const parentLayer = parent; const parentLayer = parent;
@@ -2898,7 +3042,9 @@ export class CutLayerCommand extends Command {
blendMode: sourceLayer.blendMode, blendMode: sourceLayer.blendMode,
fabricObjects: [], fabricObjects: [],
children: sourceLayer.children ? [...sourceLayer.children] : [], children: sourceLayer.children ? [...sourceLayer.children] : [],
layerProperties: sourceLayer.layerProperties ? { ...sourceLayer.layerProperties } : {}, layerProperties: sourceLayer.layerProperties
? { ...sourceLayer.layerProperties }
: {},
metadata: sourceLayer.metadata ? { ...sourceLayer.metadata } : {}, metadata: sourceLayer.metadata ? { ...sourceLayer.metadata } : {},
parentId: parentLayer ? parentLayer.id : undefined, // 保持父图层关系 parentId: parentLayer ? parentLayer.id : undefined, // 保持父图层关系
}); });
@@ -2908,8 +3054,11 @@ export class CutLayerCommand extends Command {
if (parentLayer) { if (parentLayer) {
// 处理子图层在父图层的children数组中插入 // 处理子图层在父图层的children数组中插入
const sourceChildIndex = parentLayer.children.findIndex((child) => child.id === this.layerId); const sourceChildIndex = parentLayer.children.findIndex(
insertIndex = this.insertIndex !== null ? this.insertIndex : sourceChildIndex + 1; (child) => child.id === this.layerId
);
insertIndex =
this.insertIndex !== null ? this.insertIndex : sourceChildIndex + 1;
// 插入到父图层的children数组中 // 插入到父图层的children数组中
parentLayer.children.splice(insertIndex, 0, this.newLayer); parentLayer.children.splice(insertIndex, 0, this.newLayer);
@@ -2918,8 +3067,11 @@ export class CutLayerCommand extends Command {
this.childInsertIndex = insertIndex; this.childInsertIndex = insertIndex;
} else { } else {
// 处理主图层:在主图层数组中插入 // 处理主图层:在主图层数组中插入
const sourceIndex = this.layers.value.findIndex((l) => l.id === this.layerId); const sourceIndex = this.layers.value.findIndex(
insertIndex = this.insertIndex !== null ? this.insertIndex : sourceIndex + 1; (l) => l.id === this.layerId
);
insertIndex =
this.insertIndex !== null ? this.insertIndex : sourceIndex + 1;
// 插入到主图层数组中 // 插入到主图层数组中
this.layers.value.splice(insertIndex, 0, this.newLayer); this.layers.value.splice(insertIndex, 0, this.newLayer);
@@ -2938,7 +3090,9 @@ export class CutLayerCommand extends Command {
// 重新渲染画布 // 重新渲染画布
await this.layerManager?.updateLayersObjectsInteractivity(false); await this.layerManager?.updateLayersObjectsInteractivity(false);
console.log(`已复制图层:${newName} ${this.isChildLayer ? "(子图层)" : "(主图层)"}`); console.log(
`已复制图层:${newName} ${this.isChildLayer ? "(子图层)" : "(主图层)"}`
);
return this.newLayerId; return this.newLayerId;
} }
@@ -2955,7 +3109,9 @@ export class CutLayerCommand extends Command {
} }
} else { } 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) { if (index !== -1) {
this.layers.value.splice(index, 1); this.layers.value.splice(index, 1);
} }
@@ -2985,7 +3141,9 @@ export class CutLayerCommand extends Command {
fabric.util.enlivenObjects(serializedObjects, (objects) => { fabric.util.enlivenObjects(serializedObjects, (objects) => {
objects.forEach((obj) => { objects.forEach((obj) => {
// 生成新的对象ID // 生成新的对象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.id = newObjId;
obj.layerId = this.newLayerId; obj.layerId = this.newLayerId;
obj.layerName = this.newLayer.name; obj.layerName = this.newLayer.name;
@@ -3042,7 +3200,8 @@ export class CreateAdjustmentLayerCommand extends Command {
this.newLayer = null; this.newLayer = null;
// 生成新图层ID // 生成新图层ID
this.newLayerId = 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() { execute() {
@@ -3064,7 +3223,9 @@ export class CreateAdjustmentLayerCommand extends Command {
// 计算插入位置 // 计算插入位置
const insertIndex = const insertIndex =
this.insertIndex !== null ? this.insertIndex : this._getInsertIndexAboveActiveLayer(); this.insertIndex !== null
? this.insertIndex
: this._getInsertIndexAboveActiveLayer();
// 插入新图层 // 插入新图层
this.layers.value.splice(insertIndex, 0, this.newLayer); this.layers.value.splice(insertIndex, 0, this.newLayer);
@@ -3128,7 +3289,9 @@ export class ApplyLayerStyleCommand extends Command {
return; return;
} }
this.layer = layer; this.layer = layer;
this.oldStyles = this.layer ? { ...(this.layer.layerProperties?.styles || {}) } : {}; this.oldStyles = this.layer
? { ...(this.layer.layerProperties?.styles || {}) }
: {};
} }
execute() { execute() {
@@ -3267,7 +3430,9 @@ export class LayerClippingMaskCommand extends Command {
if (this.maskLayerId) { 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) { if (!maskLayer) {
console.error(`蒙版图层 ${this.maskLayerId} 不存在`); console.error(`蒙版图层 ${this.maskLayerId} 不存在`);
return false; return false;
@@ -3494,14 +3659,19 @@ export class ChangeFixedImageCommand extends Command {
this.retryCount = attempt; this.retryCount = attempt;
if (attempt === this.maxRetries) { 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; const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay)); await new Promise((resolve) => setTimeout(resolve, delay));
console.warn(`图像加载重试 ${attempt + 1}/${this.maxRetries}:`, error.message); console.warn(
`图像加载重试 ${attempt + 1}/${this.maxRetries}:`,
error.message
);
} }
} }
} }
@@ -3509,7 +3679,9 @@ export class ChangeFixedImageCommand extends Command {
loadImage() { loadImage() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
reject(new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)); reject(
new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)
);
}, this.timeoutMs); }, this.timeoutMs);
fabric.Image.fromURL( fabric.Image.fromURL(
@@ -3547,9 +3719,15 @@ export class ChangeFixedImageCommand extends Command {
// 移除旧对象(如果存在) // 移除旧对象(如果存在)
if (this.previousObjectId) { if (this.previousObjectId) {
const { object: oldObject } = findObjectById(this.canvas, this.previousObjectId); const { object: oldObject } = findObjectById(
this.canvas,
this.previousObjectId
);
if (oldObject) { if (oldObject) {
const removeSuccess = removeCanvasObjectByObject(this.canvas, oldObject); const removeSuccess = removeCanvasObjectByObject(
this.canvas,
oldObject
);
if (!removeSuccess) { if (!removeSuccess) {
console.warn("移除旧对象失败,但继续执行"); console.warn("移除旧对象失败,但继续执行");
} }
@@ -3606,14 +3784,19 @@ export class ChangeFixedImageCommand extends Command {
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fabric.util.enlivenObjects([this.previousImage.objectData], async (objects) => { fabric.util.enlivenObjects(
[this.previousImage.objectData],
async (objects) => {
try { try {
const restoredImage = objects[0]; const restoredImage = objects[0];
await optimizeCanvasRendering(this.canvas, async () => { await optimizeCanvasRendering(this.canvas, async () => {
// 移除当前对象 // 移除当前对象
if (this.newObjectId) { if (this.newObjectId) {
const { object: currentObject } = findObjectById(this.canvas, this.newObjectId); const { object: currentObject } = findObjectById(
this.canvas,
this.newObjectId
);
if (currentObject) { if (currentObject) {
removeCanvasObjectByObject(this.canvas, currentObject); removeCanvasObjectByObject(this.canvas, currentObject);
} }
@@ -3634,7 +3817,10 @@ export class ChangeFixedImageCommand extends Command {
}); });
// 使用帮助函数在正确的z-index位置恢复对象 // 使用帮助函数在正确的z-index位置恢复对象
if (this.previousZIndex !== undefined && this.previousZIndex >= 0) { if (
this.previousZIndex !== undefined &&
this.previousZIndex >= 0
) {
const insertSuccess = insertObjectAtZIndex( const insertSuccess = insertObjectAtZIndex(
this.canvas, this.canvas,
restoredImage, restoredImage,
@@ -3657,24 +3843,38 @@ export class ChangeFixedImageCommand extends Command {
restoredImage.setCoords(); restoredImage.setCoords();
// 更新引用 // 更新引用
this.targetLayer.fabricObject = restoredImage.toObject(["id", "layerId", "type"]); this.targetLayer.fabricObject = restoredImage.toObject([
this.layerManager.updateLayerObject(this.targetLayer.id, restoredImage); "id",
"layerId",
"type",
]);
this.layerManager.updateLayerObject(
this.targetLayer.id,
restoredImage
);
}); });
resolve(); resolve();
} catch (error) { } catch (error) {
reject(error); reject(error);
} }
}); }
);
}); });
} }
async removeCurrentImage() { async removeCurrentImage() {
await optimizeCanvasRendering(this.canvas, async () => { await optimizeCanvasRendering(this.canvas, async () => {
if (this.newObjectId) { if (this.newObjectId) {
const { object: currentObject } = findObjectById(this.canvas, this.newObjectId); const { object: currentObject } = findObjectById(
this.canvas,
this.newObjectId
);
if (currentObject) { if (currentObject) {
const removeSuccess = removeCanvasObjectByObject(this.canvas, currentObject); const removeSuccess = removeCanvasObjectByObject(
this.canvas,
currentObject
);
if (removeSuccess) { if (removeSuccess) {
this.targetLayer.fabricObject = null; this.targetLayer.fabricObject = null;
this.layerManager.updateLayerObject(this.targetLayer.id, null); this.layerManager.updateLayerObject(this.targetLayer.id, null);
@@ -3731,7 +3931,10 @@ export class AddImageToLayerCommand extends Command {
this.validateInputs(); this.validateInputs();
// 查找目标图层 // 查找目标图层
const { layer } = findLayerRecursively(this.layerManager?.layers?.value || [], this.layerId); const { layer } = findLayerRecursively(
this.layerManager?.layers?.value || [],
this.layerId
);
this.targetLayer = layer; this.targetLayer = layer;
@@ -3790,7 +3993,10 @@ export class AddImageToLayerCommand extends Command {
this.canvas.remove(this.addedObject); this.canvas.remove(this.addedObject);
// 从图层管理器中移除 // 从图层管理器中移除
this.layerManager.removeObjectFromLayer(this.addedObject.id, this.layerId); this.layerManager.removeObjectFromLayer(
this.addedObject.id,
this.layerId
);
this.isExecuted = false; this.isExecuted = false;
@@ -3853,14 +4059,19 @@ export class AddImageToLayerCommand extends Command {
this.retryCount = attempt; this.retryCount = attempt;
if (attempt === this.maxRetries) { 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; const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay)); await new Promise((resolve) => setTimeout(resolve, delay));
console.warn(`图像加载重试 ${attempt + 1}/${this.maxRetries}:`, error.message); console.warn(
`图像加载重试 ${attempt + 1}/${this.maxRetries}:`,
error.message
);
} }
} }
} }
@@ -3868,7 +4079,9 @@ export class AddImageToLayerCommand extends Command {
loadImage() { loadImage() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
reject(new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)); reject(
new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)
);
}, this.timeoutMs); }, this.timeoutMs);
fabric.Image.fromURL( fabric.Image.fromURL(
@@ -3978,7 +4191,9 @@ export class RemoveChildLayerCommand extends Command {
this.parentLayer = parent; this.parentLayer = parent;
this.childIndex = 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.removedChild = this.parentLayer?.children?.[this.childIndex];
this.isActiveLayer = this.layerId === this.activeLayerId.value; this.isActiveLayer = this.layerId === this.activeLayerId.value;
@@ -3993,7 +4208,10 @@ 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) => { this.removedChild.fabricObjects.forEach((obj) => {
const { object } = findObjectById(this.canvas, obj.id); const { object } = findObjectById(this.canvas, obj.id);
if (object) { if (object) {
@@ -4012,7 +4230,9 @@ export class RemoveChildLayerCommand extends Command {
this.activeLayerId.value = this.parentLayer.children[0].id; this.activeLayerId.value = this.parentLayer.children[0].id;
} else { } else {
this.activeLayerId.value = 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;
} }
} }
@@ -4147,7 +4367,10 @@ export class ChildLayerLockCommand extends Command {
this.layerManager = options.layerManager; 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.parentLayer = parent;
this.childLayer = layer; this.childLayer = layer;
this.oldLocked = this.childLayer ? this.childLayer.locked : null; this.oldLocked = this.childLayer ? this.childLayer.locked : null;

View File

@@ -25,9 +25,13 @@ export class TransformCommand extends Command {
this.layers = options.layers || null; this.layers = options.layers || null;
this.lastSelectLayerId = options.lastSelectLayerId || null; // 最后选择的图层ID this.lastSelectLayerId = options.lastSelectLayerId || null; // 最后选择的图层ID
const targetObject = findObjectById(this.canvas, this.objectId)?.object || null; const targetObject =
findObjectById(this.canvas, this.objectId)?.object || null;
const { layer, parent } = findLayerRecursively(this.layers.value, targetObject?.layerId); const { layer, parent } = findLayerRecursively(
this.layers.value,
targetObject?.layerId
);
this.layer = layer; this.layer = layer;
this.parent = parent; this.parent = parent;
@@ -107,7 +111,7 @@ export class TransformCommand extends Command {
if ( if (
this.parent && this.parent &&
this.parent?.clippingMask && this.parent?.clippingMask &&
this.parent?.children?.length === 1 && // this.parent?.children?.length === 1 &&
this.isSginleObject this.isSginleObject
) { ) {
// 计算对象的变换位置 // 计算对象的变换位置
@@ -116,6 +120,20 @@ export class TransformCommand extends Command {
this.parent.clippingMask.left -= moveLeft; this.parent.clippingMask.left -= moveLeft;
this.parent.clippingMask.top -= moveTop; this.parent.clippingMask.top -= moveTop;
if (this.parent.selectObject) {
// 如果有选区 则选区位置也要更新
this.parent.selectObject.left = this.parent.clippingMask.left;
this.parent.selectObject.top = this.parent.clippingMask.top;
const { object } = findObjectById(
this.canvas,
this.parent.selectObject?.id
);
object?.set({
left: this.parent.clippingMask.left,
top: this.parent.clippingMask.top,
});
object?.setCoords();
}
// 重新创建遮罩对象 // 重新创建遮罩对象
const clippingMaskFabricObject = await restoreFabricObject( const clippingMaskFabricObject = await restoreFabricObject(

View File

@@ -1,3 +1,4 @@
import { findObjectById } from "../utils/helper";
import { findLayerRecursively, isGroupLayer } from "../utils/layerHelper"; import { findLayerRecursively, isGroupLayer } from "../utils/layerHelper";
import { restoreFabricObject } from "../utils/objectHelper"; import { restoreFabricObject } from "../utils/objectHelper";
import { Command } from "./Command"; import { Command } from "./Command";
@@ -54,8 +55,12 @@ export class UpdateGroupMaskPositionCommand extends Command {
}) || this.target?.getBoundingRect?.(true, true); }) || this.target?.getBoundingRect?.(true, true);
this.originalSelectionPosition = { this.originalSelectionPosition = {
left: this.isSginleObject ? this.target.left : this.activeSelection.left || 0, left: this.isSginleObject
top: this.isSginleObject ? this.target.top : this.activeSelection.top || 0, ? this.target.left
: this.activeSelection.left || 0,
top: this.isSginleObject
? this.target.top
: this.activeSelection.top || 0,
}; };
console.log( console.log(
@@ -74,6 +79,21 @@ export class UpdateGroupMaskPositionCommand extends Command {
this.layer.clippingMask.left = newLeft; this.layer.clippingMask.left = newLeft;
this.layer.clippingMask.top = newTop; this.layer.clippingMask.top = newTop;
if (this.layer.selectObject) {
// 如果有选区 则选区位置也要更新
this.layer.selectObject.left = this.layer.clippingMask.left;
this.layer.selectObject.top = this.layer.clippingMask.top;
const { object } = findObjectById(
this.canvas,
this.layer.selectObject?.id
);
object?.set({
left: this.layer.clippingMask.left,
top: this.layer.clippingMask.top,
});
object?.setCoords();
}
this.newMaskPosition = { this.newMaskPosition = {
left: newLeft, left: newLeft,
top: newTop, top: newTop,
@@ -112,6 +132,21 @@ export class UpdateGroupMaskPositionCommand extends Command {
layer.clippingMask.left = this.originalMaskPosition.left; layer.clippingMask.left = this.originalMaskPosition.left;
layer.clippingMask.top = this.originalMaskPosition.top; layer.clippingMask.top = this.originalMaskPosition.top;
if (this.layer.selectObject) {
// 如果有选区 则选区位置也要更新
this.layer.selectObject.left = this.originalMaskPosition.left;
this.layer.selectObject.top = this.originalMaskPosition.top;
const { object } = findObjectById(
this.canvas,
this.layer.selectObject?.id
);
object?.set({
left: this.originalMaskPosition.left,
top: this.originalMaskPosition.top,
});
object?.setCoords();
}
// 更新所有使用此遮罩的子图层对象 // 更新所有使用此遮罩的子图层对象
await this._updateChildObjectsClipPath(layer, true); await this._updateChildObjectsClipPath(layer, true);
// await this.layerManager.updateLayersObjectsInteractivity(); // await this.layerManager.updateLayersObjectsInteractivity();
@@ -147,6 +182,21 @@ export class UpdateGroupMaskPositionCommand extends Command {
layer.clippingMask.left = newLeft; layer.clippingMask.left = newLeft;
layer.clippingMask.top = newTop; layer.clippingMask.top = newTop;
if (this.layer.selectObject) {
// 如果有选区 则选区位置也要更新
this.layer.selectObject.left = this.layer.clippingMask.left;
this.layer.selectObject.top = this.layer.clippingMask.top;
const { object } = findObjectById(
this.canvas,
this.layer.selectObject?.id
);
object?.set({
left: this.layer.clippingMask.left,
top: this.layer.clippingMask.top,
});
object?.setCoords();
}
// 更新所有使用此遮罩的子图层对象(不需要等待) // 更新所有使用此遮罩的子图层对象(不需要等待)
this._updateChildObjectsClipPath(layer); this._updateChildObjectsClipPath(layer);
@@ -173,7 +223,10 @@ export class UpdateGroupMaskPositionCommand extends Command {
try { try {
// 重新创建遮罩对象 // 重新创建遮罩对象
const clippingMaskFabricObject = await restoreFabricObject(layer.clippingMask, this.canvas); const clippingMaskFabricObject = await restoreFabricObject(
layer.clippingMask,
this.canvas
);
if (!clippingMaskFabricObject) { if (!clippingMaskFabricObject) {
console.warn("无法恢复遮罩对象"); console.warn("无法恢复遮罩对象");
@@ -192,7 +245,9 @@ export class UpdateGroupMaskPositionCommand extends Command {
layer.children.forEach((childLayer) => { layer.children.forEach((childLayer) => {
// 更新 fabricObjects 中的对象 // 更新 fabricObjects 中的对象
childLayer.fabricObjects?.forEach((obj) => { childLayer.fabricObjects?.forEach((obj) => {
const fabricObject = this.canvas.getObjects().find((o) => o.id === obj.id); const fabricObject = this.canvas
.getObjects()
.find((o) => o.id === obj.id);
if (fabricObject) { if (fabricObject) {
fabricObject.clipPath = clippingMaskFabricObject; fabricObject.clipPath = clippingMaskFabricObject;
fabricObject.dirty = true; fabricObject.dirty = true;
@@ -215,7 +270,9 @@ export class UpdateGroupMaskPositionCommand extends Command {
if (layer?.fill) { if (layer?.fill) {
// 更新组图层的填充颜色 // 更新组图层的填充颜色
const fabricObject = this.canvas.getObjects().find((o) => o.id === layer?.fill.id); const fabricObject = this.canvas
.getObjects()
.find((o) => o.id === layer?.fill.id);
if (fabricObject) { if (fabricObject) {
fabricObject.clipPath = clippingMaskFabricObject; fabricObject.clipPath = clippingMaskFabricObject;
fabricObject.dirty = true; fabricObject.dirty = true;

View File

@@ -1,5 +1,7 @@
import { fabric } from "fabric-with-all"; import { fabric } from "fabric-with-all";
import initAligningGuidelines, { initCenteringGuidelines } from "../utils/helperLine"; import initAligningGuidelines, {
initCenteringGuidelines,
} from "../utils/helperLine";
import { ThumbnailManager } from "./ThumbnailManager"; import { ThumbnailManager } from "./ThumbnailManager";
import { ExportManager } from "./ExportManager"; import { ExportManager } from "./ExportManager";
import { import {
@@ -14,7 +16,12 @@ import { CanvasEventManager } from "./events/CanvasEventManager";
import CanvasConfig from "../config/canvasConfig"; import CanvasConfig from "../config/canvasConfig";
import { RedGreenModeManager } from "./RedGreenModeManager"; import { RedGreenModeManager } from "./RedGreenModeManager";
import { EraserStateManager } from "./EraserStateManager"; import { EraserStateManager } from "./EraserStateManager";
import { deepClone, generateId, optimizeCanvasRendering } from "../utils/helper"; import {
deepClone,
findObjectById,
generateId,
optimizeCanvasRendering,
} from "../utils/helper";
import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands"; import { ChangeFixedImageCommand } from "../commands/ObjectLayerCommands";
import { isFunction } from "lodash-es"; import { isFunction } from "lodash-es";
import { import {
@@ -150,7 +157,10 @@ export class CanvasManager {
return false; return false;
}; };
this.eraserStateManager = new EraserStateManager(this.canvas, this.layerManager); this.eraserStateManager = new EraserStateManager(
this.canvas,
this.layerManager
);
// 监听擦除开始事件 // 监听擦除开始事件
this.canvas.on("erasing:start", () => { this.canvas.on("erasing:start", () => {
@@ -171,12 +181,17 @@ export class CanvasManager {
} }
// 更新交互性 // 更新交互性
command && (await this.layerManager?.updateLayersObjectsInteractivity?.()); command &&
(await this.layerManager?.updateLayersObjectsInteractivity?.());
this.thumbnailManager?.generateLayerThumbnail(this.layerManager?.activeLayerId?.value); this.thumbnailManager?.generateLayerThumbnail(
this.layerManager?.activeLayerId?.value
);
// 固定图层 的擦除也需要重新生成缩略图 要判断 当前固定图层是否锁定 // 固定图层 的擦除也需要重新生成缩略图 要判断 当前固定图层是否锁定
const fixedLayer = this.layers?.value?.find((layer) => layer.isFixed && !layer.locked); const fixedLayer = this.layers?.value?.find(
(layer) => layer.isFixed && !layer.locked
);
// 如果有固定图层且未锁定,则生成缩略图 // 如果有固定图层且未锁定,则生成缩略图
fixedLayer && fixedLayer &&
this.isFixedErasable && this.isFixedErasable &&
@@ -366,7 +381,9 @@ export class CanvasManager {
// 设置固定图层的可擦除状态 // 设置固定图层的可擦除状态
layer.locked = flag; layer.locked = flag;
// 更新画布对象的erasable属性 // 更新画布对象的erasable属性
const fabricObject = this.canvas.getObjects().find((obj) => obj.id === layer.id); const fabricObject = this.canvas
.getObjects()
.find((obj) => obj.id === layer.id);
if (fabricObject) { if (fabricObject) {
fabricObject.erasable = flag; fabricObject.erasable = flag;
fabricObject.set("erasable", flag); fabricObject.set("erasable", flag);
@@ -478,7 +495,9 @@ export class CanvasManager {
const deltaY = backgroundObject.top - backgroundOldTop; const deltaY = backgroundObject.top - backgroundOldTop;
// 将相同的偏移量应用到所有其他对象上 // 将相同的偏移量应用到所有其他对象上
const otherObjects = visibleObjects.filter((obj) => obj !== backgroundObject); const otherObjects = visibleObjects.filter(
(obj) => obj !== backgroundObject
);
otherObjects.forEach((obj) => { otherObjects.forEach((obj) => {
obj.set({ obj.set({
@@ -496,12 +515,29 @@ export class CanvasManager {
// 如果图层有遮罩,更新遮罩位置 // 如果图层有遮罩,更新遮罩位置
layer.clippingMask.left += deltaX; layer.clippingMask.left += deltaX;
layer.clippingMask.top += deltaY; layer.clippingMask.top += deltaY;
if (layer.selectObject) {
// 如果有选区 则选区位置也要更新
layer.selectObject.left = layer.clippingMask.left;
layer.selectObject.top = layer.clippingMask.top;
const { object } = findObjectById(
this.canvas,
layer.selectObject?.id
);
object?.set({
left: layer.clippingMask.left,
top: layer.clippingMask.top,
});
object?.setCoords();
}
} }
}); });
if (isMaskLayer) { if (isMaskLayer) {
requestAnimationFrame(() => { setTimeout(() => {
this.layerManager?.updateLayersObjectsInteractivity?.(); this.layerManager?.updateLayersObjectsInteractivity?.(false, {
isMoveing: true,
});
}); });
} }
@@ -598,7 +634,8 @@ export class CanvasManager {
if (!backgroundLayerObject) return; if (!backgroundLayerObject) return;
const bgWidth = backgroundLayerObject.width * backgroundLayerObject.scaleX; const bgWidth = backgroundLayerObject.width * backgroundLayerObject.scaleX;
const bgHeight = backgroundLayerObject.height * backgroundLayerObject.scaleY; const bgHeight =
backgroundLayerObject.height * backgroundLayerObject.scaleY;
const left = backgroundLayerObject.left; const left = backgroundLayerObject.left;
const top = backgroundLayerObject.top; const top = backgroundLayerObject.top;
@@ -659,7 +696,9 @@ export class CanvasManager {
return obj.isBackground || obj.id === backgroundLayerId; return obj.isBackground || obj.id === backgroundLayerId;
}); });
if (!backgroundLayerByBgLayer) { if (!backgroundLayerByBgLayer) {
console.warn("CanvasManager.js = >getBackgroundLayer 方法没有找到背景层"); console.warn(
"CanvasManager.js = >getBackgroundLayer 方法没有找到背景层"
);
} }
return backgroundLayerByBgLayer; return backgroundLayerByBgLayer;
@@ -669,7 +708,8 @@ export class CanvasManager {
* @param {Object} backgroundLayerObject 背景层对象 * @param {Object} backgroundLayerObject 背景层对象
*/ */
updateMaskPosition(backgroundLayerObject) { updateMaskPosition(backgroundLayerObject) {
if (!backgroundLayerObject || !this.maskLayer || !this.canvas.clipPath) return; if (!backgroundLayerObject || !this.maskLayer || !this.canvas.clipPath)
return;
const left = backgroundLayerObject.left; const left = backgroundLayerObject.left;
const top = backgroundLayerObject.top; const top = backgroundLayerObject.top;
@@ -734,7 +774,8 @@ export class CanvasManager {
...options, ...options,
}); });
command.undoable = options.undoable !== undefined ? options.undoable : false; // 默认不可撤销 undoable = true 为可撤销 command.undoable =
options.undoable !== undefined ? options.undoable : false; // 默认不可撤销 undoable = true 为可撤销
const result = (await command?.execute?.()) || { const result = (await command?.execute?.()) || {
success: false, success: false,
@@ -784,7 +825,9 @@ export class CanvasManager {
...options, ...options,
// 如果没有明确指定,则根据当前模式自动设置 // 如果没有明确指定,则根据当前模式自动设置
restoreOpacityInRedGreen: restoreOpacityInRedGreen:
options.restoreOpacityInRedGreen !== undefined ? options.restoreOpacityInRedGreen : true, // 默认在红绿图模式下恢复透明度 options.restoreOpacityInRedGreen !== undefined
? options.restoreOpacityInRedGreen
: true, // 默认在红绿图模式下恢复透明度
}; };
// 如果在红绿图模式下且没有指定具体的图层,自动包含所有普通图层 // 如果在红绿图模式下且没有指定具体的图层,自动包含所有普通图层
@@ -798,7 +841,9 @@ export class CanvasManager {
// 获取所有非背景、非固定的普通图层ID // 获取所有非背景、非固定的普通图层ID
const normalLayerIds = const normalLayerIds =
this.layers?.value this.layers?.value
?.filter((layer) => !layer.isBackground && !layer.isFixed && layer.visible) ?.filter(
(layer) => !layer.isBackground && !layer.isFixed && layer.visible
)
?.map((layer) => layer.id) || []; ?.map((layer) => layer.id) || [];
if (normalLayerIds.length > 0) { if (normalLayerIds.length > 0) {
@@ -911,7 +956,9 @@ export class CanvasManager {
this.canvas.discardActiveObject(); this.canvas.discardActiveObject();
this.canvas.renderAll(); this.canvas.renderAll();
const simplifyLayersData = simplifyLayers(JSON.parse(JSON.stringify(this.layers.value))); const simplifyLayersData = simplifyLayers(
JSON.parse(JSON.stringify(this.layers.value))
);
console.log("获取画布JSON数据...", simplifyLayersData); console.log("获取画布JSON数据...", simplifyLayersData);
return JSON.stringify({ return JSON.stringify({
canvas: this.canvas.toJSON([ canvas: this.canvas.toJSON([
@@ -927,6 +974,7 @@ export class CanvasManager {
"eraser", "eraser",
"eraserable", "eraserable",
"erasable", "erasable",
"customType",
]), ]),
layers: simplifyLayersData, // 简化图层数据 layers: simplifyLayersData, // 简化图层数据
// layers: JSON.stringify(JSON.parse(JSON.stringify(this.layers.value))), // 全数据 // layers: JSON.stringify(JSON.parse(JSON.stringify(this.layers.value))), // 全数据
@@ -1033,7 +1081,8 @@ export class CanvasManager {
// 使用LayerSort工具重新排列画布对象如果可用 // 使用LayerSort工具重新排列画布对象如果可用
await this?.layerManager?.layerSort?.rearrangeObjects(); await this?.layerManager?.layerSort?.rearrangeObjects();
this.layerManager.activeLayerId.value = this.layers.value[0].children?.length this.layerManager.activeLayerId.value = this.layers.value[0]
.children?.length
? this.layers.value[0].children[0].id ? this.layers.value[0].children[0].id
: this.layers.value[0]?.id || parsedJson?.activeLayerId || null; : this.layers.value[0]?.id || parsedJson?.activeLayerId || null;
@@ -1046,7 +1095,9 @@ export class CanvasManager {
await calllBack?.(); await calllBack?.();
// 确保所有对象的交互性正确设置 // 确保所有对象的交互性正确设置
await this.layerManager?.updateLayersObjectsInteractivity?.(false); await this.layerManager?.updateLayersObjectsInteractivity?.(
false
);
console.log(this.layerManager.layers.value); console.log(this.layerManager.layers.value);
// 更新所有缩略图 // 更新所有缩略图
@@ -1203,7 +1254,9 @@ export class CanvasManager {
if (!this.layers || !this.layers.value) return []; if (!this.layers || !this.layers.value) return [];
// 查找所有非背景、非固定的普通图层 // 查找所有非背景、非固定的普通图层
const normalLayers = this.layers.value.filter((layer) => !layer.isBackground && !layer.isFixed); const normalLayers = this.layers.value.filter(
(layer) => !layer.isBackground && !layer.isFixed
);
// 收集所有普通图层中的对象 // 收集所有普通图层中的对象
const objects = []; const objects = [];
@@ -1240,9 +1293,15 @@ export class CanvasManager {
// 比较尺寸允许5%的误差) // 比较尺寸允许5%的误差)
const sizeMatch = const sizeMatch =
Math.abs(obj.width * obj.scaleX - fixedLayerObject.width * fixedLayerObject.scaleX) < Math.abs(
obj.width * obj.scaleX -
fixedLayerObject.width * fixedLayerObject.scaleX
) <
fixedLayerObject.width * fixedLayerObject.scaleX * 0.05 && fixedLayerObject.width * fixedLayerObject.scaleX * 0.05 &&
Math.abs(obj.height * obj.scaleY - fixedLayerObject.height * fixedLayerObject.scaleY) < Math.abs(
obj.height * obj.scaleY -
fixedLayerObject.height * fixedLayerObject.scaleY
) <
fixedLayerObject.height * fixedLayerObject.scaleY * 0.05; fixedLayerObject.height * fixedLayerObject.scaleY * 0.05;
// 比较位置(允许一定的偏差) // 比较位置(允许一定的偏差)

View File

@@ -64,6 +64,7 @@ import { isBoolean, template } from "lodash-es";
import { import {
findObjectById, findObjectById,
generateId, generateId,
insertObjectAtZIndex,
optimizeCanvasRendering, optimizeCanvasRendering,
} from "../utils/helper"; } from "../utils/helper";
import { message } from "ant-design-vue"; import { message } from "ant-design-vue";
@@ -189,19 +190,40 @@ export class LayerManager {
* 根据当前编辑模式和图层状态设置对象的交互属性 * 根据当前编辑模式和图层状态设置对象的交互属性
* @private * @private
*/ */
async updateLayersObjectsInteractivity(isUseOptimize = true) { async updateLayersObjectsInteractivity(
isUseOptimize = true,
{ isMoveing = false } = {}
) {
if (!this.canvas) return; if (!this.canvas) return;
if (isUseOptimize) { if (isUseOptimize) {
// 优化渲染 - 统一批处理 支持异步回调 // 优化渲染 - 统一批处理 支持异步回调
await optimizeCanvasRendering(this.canvas, async () => { await optimizeCanvasRendering(this.canvas, async () => {
// 应用图层交互规则 // 应用图层交互规则
await this._applyInteractionRules(); await this._applyInteractionRules({ isMoveing });
}); });
} else { } else {
// 直接应用图层交互规则 // 直接应用图层交互规则
await this._applyInteractionRules(); await this._applyInteractionRules({ isMoveing });
} }
// customType: "selectionObject",
// 清空没有关联的选区图层
const selectionObjects = this.canvas
.getObjects()
?.filter((obj) => obj.customType == "selectionObject");
selectionObjects.forEach((obj) => {
const isObjExtis = this.layers?.value.find(
(layer) => layer?.selectObject?.id == obj.id
);
if (!isObjExtis) {
// 不存在则移除对象
this?.canvas?.remove(obj);
}
});
selectionObjects && this.canvas.renderAll();
// // 性能优化使用requestAnimationFrame // // 性能优化使用requestAnimationFrame
// requestAnimationFrame(() => { // requestAnimationFrame(() => {
// // 暂停渲染以提高性能 // // 暂停渲染以提高性能
@@ -304,7 +326,7 @@ export class LayerManager {
} }
// 私有方法:应用交互规则 // 私有方法:应用交互规则
async _applyInteractionRules() { async _applyInteractionRules({ isMoveing }) {
console.log("updateLayersObjectsInteractivity ===>", this.editorMode); console.log("updateLayersObjectsInteractivity ===>", this.editorMode);
const objects = this.canvas.getObjects(); const objects = this.canvas.getObjects();
const editorMode = this.editorMode || CanvasConfig.defaultTool; const editorMode = this.editorMode || CanvasConfig.defaultTool;
@@ -414,6 +436,68 @@ export class LayerManager {
// } // }
// } // }
} }
// 设置画布选区内容
if (layer.selectObject && !isMoveing) {
// 如果有选区对象
let insetIndex = objects.findIndex(
(o) => o.id === layer.selectObject.id
);
let isOldSelectObject = insetIndex >= 0; // 是否存在旧的选区对象
const newSelectObject = await restoreFabricObject(
layer.selectObject,
this.canvas
);
if (insetIndex < 0) {
// 如果没有找到选区对象,则添加到画布 先查找子图层元素下标 如果没有则要向前查找有元素的下标 否则直接添加,因为选区要遮罩在选区图层的最上面
layer.children?.forEach((childLayer) => {
const childObj = this.canvas
.getObjects()
.find((o) => o.layerId === childLayer.id);
if (childObj) {
insetIndex = objects.indexOf(childObj);
}
});
}
// 向前查找,找不到则添加到最后
if (insetIndex < 0) {
// 如果没有找到选区对象,则添加到画布
const layerIndex = this.layers.value.findIndex(
(ll) => ll.id === layer.id
);
for (let i = layerIndex - 1; i >= 0; i--) {
const l = this.layers.value[i];
if (l.fabricObjects?.length > 0) {
insetIndex = objects.findIndex(
(o) => o.id === l.fabricObjects?.[0].id
);
if (insetIndex >= 0) break; // 找到第一个有对象的图层
}
}
insetIndex = insetIndex < 0 ? objects.length : insetIndex;
}
newSelectObject.set({
id: layer.selectObject.id,
customType: layer.selectObject.customType,
selectable: false,
evented: false,
erasable: false,
});
// 有则更新对象
// 没有则反序列创建新的选区对象
insertObjectAtZIndex(
this.canvas,
newSelectObject,
insetIndex,
false,
isOldSelectObject
);
}
}); });
} }
@@ -871,6 +955,7 @@ export class LayerManager {
layers: this.layers, layers: this.layers,
layerId: layerId, layerId: layerId,
activeLayerId: this.activeLayerId, activeLayerId: this.activeLayerId,
layerManager: this,
}); });
// 执行命令 // 执行命令

View File

@@ -15,13 +15,18 @@ export function buildLayerAssociations(layer, canvasObjects) {
// 处理单个fabricObject关联 // 处理单个fabricObject关联
if (layer.fabricObject) { if (layer.fabricObject) {
// 如果图层已经有关联的fabricObject确保它的layerId和layerName正确 // 如果图层已经有关联的fabricObject确保它的layerId和layerName正确
layer.fabricObject = canvasObjects.find((obj) => obj.id === layer.fabricObject.id) || null; layer.fabricObject =
canvasObjects.find((obj) => obj.id === layer.fabricObject.id) || null;
} }
if (layer.clippingMask) { if (layer.clippingMask) {
// clippingMask 可能是一个fabricObject或组 也可能是一个简单对象 // clippingMask 可能是一个fabricObject或组 也可能是一个简单对象
const clippingMaskObj = canvasObjects.find((obj) => obj.id === layer.clippingMask.id); const clippingMaskObj = canvasObjects.find(
layer.clippingMask = clippingMaskObj ? clippingMaskObj?.toObject?.(["id"]) : layer.clippingMask; (obj) => obj.id === layer.clippingMask.id
);
layer.clippingMask = clippingMaskObj
? clippingMaskObj?.toObject?.(["id"])
: layer.clippingMask;
} }
// 处理多个fabricObjects关联 // 处理多个fabricObjects关联
@@ -167,7 +172,10 @@ export function simplifyLayers(layers) {
opacity: layer.opacity, opacity: layer.opacity,
isBackground: layer.isBackground || false, isBackground: layer.isBackground || false,
isFixed: layer.isFixed || false, isFixed: layer.isFixed || false,
clippingMask: layer.clippingMask?.toObject?.(["id", "layerId"]) || layer.clippingMask || null, // 可能是一个fabricObject或组 也可能是一个简单对象 clippingMask:
layer.clippingMask?.toObject?.(["id", "layerId"]) ||
layer.clippingMask ||
null, // 可能是一个fabricObject或组 也可能是一个简单对象
// ? { // ? {
// id: layer.clippingMask.id, // id: layer.clippingMask.id,
// type: layer.clippingMask.type, // type: layer.clippingMask.type,
@@ -192,9 +200,13 @@ export function simplifyLayers(layers) {
) )
.filter((obj) => obj !== null) .filter((obj) => obj !== null)
: [], : [],
children: layer.children && isArray(layer.children) ? simplifyLayers(layer.children) : [], children:
layer.children && isArray(layer.children)
? simplifyLayers(layer.children)
: [],
fill: layer?.fill || null, fill: layer?.fill || null,
fillColor: layer.fillColor, fillColor: layer.fillColor,
selectObject: layer.selectObject,
}; };
return simplifiedLayer; return simplifiedLayer;
@@ -231,7 +243,9 @@ export function restoreLayers(simplifiedLayers, canvasObjects) {
if (layer.clippingMask) { if (layer.clippingMask) {
// clippingMask 可能是一个fabricObject或组 也可能是一个简单对象 // clippingMask 可能是一个fabricObject或组 也可能是一个简单对象
const clippingMaskObj = canvasObjects.find((obj) => obj.id === layer.clippingMask.id); const clippingMaskObj = canvasObjects.find(
(obj) => obj.id === layer.clippingMask.id
);
restoredLayer.clippingMask = clippingMaskObj restoredLayer.clippingMask = clippingMaskObj
? clippingMaskObj?.toObject?.(["id"]) ? clippingMaskObj?.toObject?.(["id"])
: layer.clippingMask; : layer.clippingMask;
@@ -239,11 +253,17 @@ export function restoreLayers(simplifiedLayers, canvasObjects) {
// 恢复单个fabricObject关联 // 恢复单个fabricObject关联
if (layer.fabricObject?.id) { if (layer.fabricObject?.id) {
const fabricObj = canvasObjects.find((obj) => obj.id === layer.fabricObject.id); const fabricObj = canvasObjects.find(
(obj) => obj.id === layer.fabricObject.id
);
if (fabricObj) { if (fabricObj) {
fabricObj.layerId = layer.id; fabricObj.layerId = layer.id;
fabricObj.layerName = layer.name; fabricObj.layerName = layer.name;
restoredLayer.fabricObject = fabricObj.toObject(["id", "layerId", "type"]); restoredLayer.fabricObject = fabricObj.toObject([
"id",
"layerId",
"type",
]);
} else { } else {
restoredLayer.fabricObject = null; restoredLayer.fabricObject = null;
} }
@@ -253,7 +273,9 @@ export function restoreLayers(simplifiedLayers, canvasObjects) {
if (layer.fabricObjects && isArray(layer.fabricObjects)) { if (layer.fabricObjects && isArray(layer.fabricObjects)) {
restoredLayer.fabricObjects = layer.fabricObjects restoredLayer.fabricObjects = layer.fabricObjects
.map((fabricRef) => { .map((fabricRef) => {
const fabricObj = canvasObjects.find((obj) => obj.id === fabricRef.id); const fabricObj = canvasObjects.find(
(obj) => obj.id === fabricRef.id
);
if (fabricObj) { if (fabricObj) {
fabricObj.layerId = layer.id; fabricObj.layerId = layer.id;
fabricObj.layerName = layer.name; fabricObj.layerName = layer.name;