feat : 显示选区逻辑完成
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
// 比较位置(允许一定的偏差)
|
// 比较位置(允许一定的偏差)
|
||||||
|
|||||||
@@ -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,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 执行命令
|
// 执行命令
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user