feat: 优化选区功能,修复部分bug
This commit is contained in:
@@ -35,11 +35,13 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
|
||||
this.highResolutionEnabled = options.highResolutionEnabled !== false; // 默认启用
|
||||
this.baseResolutionScale = options.baseResolutionScale || 2; // 基础分辨率倍数
|
||||
|
||||
this.groupId = options.groupId || `cutout-group-${Date.now()}`;
|
||||
this.groupId = options.groupId || generateId("lasso-copy-group-");
|
||||
this.groupName = options.groupName || `选区组`;
|
||||
this.groupLayer = null; // 新增:保存组图层的引用
|
||||
this.originalLayersLength = 0; // 新增:保存原始图层数量
|
||||
|
||||
this.clippingMaskId = generateId("clipping-mask-");
|
||||
|
||||
// 在初始化时克隆保存选区对象,避免撤销后重做时获取不到选区对象
|
||||
this._clonedSelectionObject = null;
|
||||
this._initializeClonedSelection();
|
||||
@@ -105,6 +107,16 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
|
||||
return false;
|
||||
}
|
||||
|
||||
const clippingMask = fabric.util.object.clone(selectionObject);
|
||||
clippingMask.set({
|
||||
id: this.clippingMaskId,
|
||||
selectable: false,
|
||||
evented: false,
|
||||
hasControls: false,
|
||||
// layerId: this.groupId,
|
||||
visible: true,
|
||||
});
|
||||
|
||||
// 获取选区边界信息用于后续定位
|
||||
const selectionBounds = selectionObject.getBoundingRect(true, true);
|
||||
|
||||
@@ -185,12 +197,8 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
|
||||
selectLayer.fabricObjects = [
|
||||
this.fabricImage.toObject("id", "layerId", "layerName", "parentId"),
|
||||
];
|
||||
this.groupLayer.clippingMask = this.fabricImage.toObject(
|
||||
"id",
|
||||
"layerId",
|
||||
"layerName",
|
||||
"parentId"
|
||||
); // 设置组图层的fabricObject为遮罩图像
|
||||
|
||||
this.groupLayer.clippingMask = clippingMask.toObject(["id", "layerId"]); // 设置组图层的fabricObject为遮罩图像
|
||||
|
||||
this.groupLayer.children.push(selectLayer);
|
||||
// 插入新组图层
|
||||
|
||||
1323
src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.bak.js
Normal file
1323
src/component/Canvas/CanvasEditor/commands/LassoCutoutCommand.bak.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@ import { generateId } from "../utils/helper.js";
|
||||
|
||||
/**
|
||||
* 套索抠图命令
|
||||
* 实现将选区内容抠图到新图层的功能
|
||||
* 实现将选区内容到新图层遮罩
|
||||
*/
|
||||
export class LassoCutoutCommand extends CompositeCommand {
|
||||
constructor(options = {}) {
|
||||
@@ -36,8 +36,11 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
this.highResolutionEnabled = options.highResolutionEnabled !== false; // 默认启用
|
||||
this.baseResolutionScale = options.baseResolutionScale || 2; // 基础分辨率倍数
|
||||
|
||||
this.groupId = options.groupId || `cutout-group-${Date.now()}`;
|
||||
this.groupId = options.groupId || generateId("lasso-group-");
|
||||
this.groupName = options.groupName || `选区组`;
|
||||
|
||||
this.clippingMaskId = generateId("clipping-mask-");
|
||||
|
||||
this.groupLayer = null; // 新增:保存组图层的引用
|
||||
this.originalLayersLength = 0; // 新增:保存原始图层数量
|
||||
|
||||
@@ -51,6 +54,8 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
this.originalFabricObjects = []; // 保存原图层的所有fabric对象(序列化)
|
||||
this.originalCanvasObjects = []; // 保存从画布中获取的真实fabric对象
|
||||
this._initializeOriginalLayerInfo();
|
||||
|
||||
this.oldActiveLayerId = this.layerManager.activeLayerId.value; // 保存旧的活动图层ID
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,14 +149,24 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
}
|
||||
|
||||
// 获取源图层的所有对象(包括子图层)
|
||||
const sourceObjects = this._getLayerObjects(sourceLayer);
|
||||
if (sourceObjects.length === 0) {
|
||||
console.error("无法执行套索抠图:源图层没有可见对象");
|
||||
return false;
|
||||
}
|
||||
// const sourceObjects = this._getLayerObjects(sourceLayer);
|
||||
// if (sourceObjects.length === 0) {
|
||||
// console.error("无法执行套索抠图:源图层没有可见对象");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
const clippingMask = fabric.util.object.clone(selectionObject);
|
||||
clippingMask.set({
|
||||
id: this.clippingMaskId,
|
||||
selectable: false,
|
||||
evented: false,
|
||||
hasControls: false,
|
||||
// layerId: this.groupId,
|
||||
visible: true,
|
||||
});
|
||||
|
||||
// 获取选区边界信息用于后续定位
|
||||
const selectionBounds = selectionObject.getBoundingRect(true, true);
|
||||
// const selectionBounds = selectionObject.getBoundingRect(true, true);
|
||||
|
||||
// 使用createRasterizedImage执行抠图操作
|
||||
// this.cutoutImageUrl = await this._performCutoutWithRasterized(
|
||||
@@ -164,46 +179,34 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
this.fabricImage = await this._performCutoutWithRasterized(
|
||||
sourceObjects,
|
||||
selectionObject,
|
||||
selectionBounds
|
||||
);
|
||||
// this.fabricImage = await this._performCutoutWithRasterized(
|
||||
// sourceObjects,
|
||||
// selectionObject,
|
||||
// selectionBounds
|
||||
// );
|
||||
|
||||
// // 创建fabric图像对象,传递选区边界信息
|
||||
// this.fabricImage = await this._createFabricImage(
|
||||
// this.cutoutImageUrl,
|
||||
// selectionBounds
|
||||
// );
|
||||
if (!this.fabricImage) {
|
||||
console.error("创建图像对象失败");
|
||||
return false;
|
||||
}
|
||||
// if (!this.fabricImage) {
|
||||
// console.error("创建图像对象失败");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// 1. 创建图像图层命令
|
||||
const createImageLayerCmd = new CreateImageLayerCommand({
|
||||
layerManager: this.layerManager,
|
||||
fabricImage: this.fabricImage,
|
||||
toolManager: this.toolManager,
|
||||
layerName: this.newLayerName,
|
||||
});
|
||||
// const createImageLayerCmd = new CreateImageLayerCommand({
|
||||
// layerManager: this.layerManager,
|
||||
// fabricImage: this.fabricImage,
|
||||
// toolManager: this.toolManager,
|
||||
// layerName: this.newLayerName,
|
||||
// });
|
||||
|
||||
// 执行创建图像图层命令
|
||||
const result = await createImageLayerCmd.execute();
|
||||
this.newLayerId = createImageLayerCmd.newLayerId;
|
||||
this.executedCommands.push(createImageLayerCmd);
|
||||
|
||||
// 2. 删除原图层命令
|
||||
const removeOriginalLayerCmd = new RemoveLayerCommand({
|
||||
canvas: this.canvas,
|
||||
layers: this.layerManager.layers,
|
||||
layerId: this.originalLayer.id,
|
||||
activeLayerId: this.layerManager.activeLayerId,
|
||||
});
|
||||
|
||||
// 执行删除原图层命令
|
||||
await removeOriginalLayerCmd.execute();
|
||||
this.executedCommands.push(removeOriginalLayerCmd);
|
||||
// // 执行创建图像图层命令
|
||||
// const result = await createImageLayerCmd.execute();
|
||||
// this.newLayerId = createImageLayerCmd.newLayerId;
|
||||
// this.executedCommands.push(createImageLayerCmd);
|
||||
|
||||
// 3. 清除选区命令
|
||||
const clearSelectionCmd = new ClearSelectionCommand({
|
||||
@@ -216,10 +219,10 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
this.executedCommands.push(clearSelectionCmd);
|
||||
|
||||
const topLayerIndex = this.layerManager.layers.value.findIndex(
|
||||
(layer) => layer.id === this.newLayerId
|
||||
(layer) => layer.id === this.originalLayer.id
|
||||
);
|
||||
|
||||
const selectLayer = this.layerManager.layers.value[topLayerIndex];
|
||||
// const selectLayer = this.layerManager.layers.value[topLayerIndex];
|
||||
|
||||
// 创建新的组图层
|
||||
this.groupLayer = createLayer({
|
||||
@@ -233,28 +236,47 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
children: [],
|
||||
});
|
||||
|
||||
this.fabricImage.set({
|
||||
selectable: true,
|
||||
evented: true,
|
||||
// this.fabricImage.set({
|
||||
// selectable: true,
|
||||
// evented: true,
|
||||
// });
|
||||
|
||||
const selectLayer = createLayer({
|
||||
name: `选区空图层`,
|
||||
type: LayerType.EMPTY,
|
||||
visible: true,
|
||||
locked: false,
|
||||
opacity: 1.0,
|
||||
fabricObjects: [],
|
||||
children: [],
|
||||
});
|
||||
|
||||
selectLayer.parentId = this.groupId; // 设置新图层的parentId为组图层ID
|
||||
selectLayer.fabricObjects = [
|
||||
this.fabricImage.toObject("id", "layerId", "layerName", "parentId"),
|
||||
];
|
||||
this.groupLayer.clippingMask = this.fabricImage.toObject(
|
||||
"id",
|
||||
"layerId",
|
||||
"layerName",
|
||||
"parentId"
|
||||
); // 设置组图层的fabricObject为遮罩图像
|
||||
// selectLayer.fabricObjects = [
|
||||
// this.fabricImage.toObject("id", "layerId", "layerName", "parentId"),
|
||||
// ];
|
||||
// 2. 删除原图层命令
|
||||
const removeOriginalLayerCmd = new RemoveLayerCommand({
|
||||
canvas: this.canvas,
|
||||
layers: this.layerManager.layers,
|
||||
layerId: this.originalLayer.id,
|
||||
activeLayerId: this.layerManager.activeLayerId,
|
||||
});
|
||||
|
||||
// 执行删除原图层命令
|
||||
await removeOriginalLayerCmd.execute();
|
||||
this.executedCommands.push(removeOriginalLayerCmd);
|
||||
|
||||
this.groupLayer.clippingMask = clippingMask.toObject(["id", "layerId"]); // 设置组图层的fabricObject为遮罩图像
|
||||
|
||||
this.groupLayer.children.push(selectLayer);
|
||||
// 插入新组图层
|
||||
this.layerManager.layers.value.splice(topLayerIndex, 1, this.groupLayer);
|
||||
this.layerManager.layers.value.splice(topLayerIndex, 0, this.groupLayer);
|
||||
|
||||
this.layerManager.activeLayerId.value = selectLayer.id; // 设置新组图层为活动图层
|
||||
|
||||
this.canvas.discardActiveObject();
|
||||
this.canvas.setActiveObject(this.fabricImage);
|
||||
// this.canvas.setActiveObject(this.fabricImage);
|
||||
await this.layerManager.updateLayersObjectsInteractivity(true);
|
||||
|
||||
console.log(`套索抠图完成,新图层ID: ${this.newLayerId}`);
|
||||
@@ -328,6 +350,8 @@ export class LassoCutoutCommand extends CompositeCommand {
|
||||
}
|
||||
}
|
||||
|
||||
this.layerManager.activeLayerId.value = this.oldActiveLayerId; // 恢复旧的活动图层ID
|
||||
|
||||
if (this.fabricImage) {
|
||||
console.log(`↩️ 移除抠图图像: ${this.fabricImage.id}`);
|
||||
// 从画布中移除抠图图像
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from "../utils/helper";
|
||||
import { createRasterizedImage } from "../utils/rasterizedImage";
|
||||
import { message } from "ant-design-vue";
|
||||
import { restoreFabricObject } from "../utils/objectHelper";
|
||||
|
||||
/**
|
||||
* 栅格化图层命令
|
||||
@@ -82,7 +83,7 @@ export class RasterizeLayerCommand extends Command {
|
||||
this.canvas.renderAll();
|
||||
|
||||
// 检查是否有遮罩对象
|
||||
const maskObject = this._getMaskObject();
|
||||
const maskObject = await this._getMaskObject();
|
||||
|
||||
// 创建栅格化图像
|
||||
const rasterizedImage = await createRasterizedImage({
|
||||
@@ -392,9 +393,9 @@ export class RasterizeLayerCommand extends Command {
|
||||
* @returns {Object|null} 遮罩对象或null
|
||||
* @private
|
||||
*/
|
||||
_getMaskObject() {
|
||||
async _getMaskObject() {
|
||||
// 如果图层有clippingMask,获取对应的fabric对象
|
||||
if (this.layer?.clippingMask?.id) {
|
||||
if (this.layer?.clippingMask) {
|
||||
const { object: maskObject } = findObjectById(
|
||||
this.canvas,
|
||||
this.layer.clippingMask.id
|
||||
@@ -405,6 +406,7 @@ export class RasterizeLayerCommand extends Command {
|
||||
);
|
||||
return maskObject;
|
||||
}
|
||||
return await restoreFabricObject(this.layer.clippingMask);
|
||||
}
|
||||
|
||||
console.log("📎 未找到遮罩对象");
|
||||
|
||||
@@ -601,8 +601,8 @@ function handleLayerClick(layer, event) {
|
||||
layer.children.length > 0
|
||||
) {
|
||||
// 如果是组图层,设置第一个子图层为活动图层
|
||||
setActiveLayer(layer.children[0].id, { parentId: layer.id });
|
||||
layerManager?.setAllActiveGroupLayerCanvasObject?.(layer);
|
||||
setActiveLayer(layer.children[0].id, { parentId: layer.id });
|
||||
} else {
|
||||
// 否则直接设置当前图层为活动图层
|
||||
setActiveLayer(layer.id);
|
||||
|
||||
@@ -39,7 +39,6 @@ import {
|
||||
loadImageUrlToLayer,
|
||||
loadImage,
|
||||
} from "./utils/imageHelper.js";
|
||||
import { next } from "lodash-es";
|
||||
// import MinimapPanel from "./components/MinimapPanel.vue";
|
||||
const KeyboardShortcutHelp = defineAsyncComponent(() =>
|
||||
import("./components/KeyboardShortcutHelp.vue")
|
||||
|
||||
@@ -407,8 +407,8 @@ export class CanvasManager {
|
||||
// 居中所有画布元素,包括背景层和其他元素
|
||||
this.centerAllObjects();
|
||||
|
||||
// 重新渲染画布使变更生效
|
||||
this.canvas.renderAll();
|
||||
// // 重新渲染画布使变更生效
|
||||
// this.canvas.renderAll();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -510,7 +510,7 @@ export class CanvasManager {
|
||||
}
|
||||
|
||||
// 重新渲染画布
|
||||
this.canvas.renderAll();
|
||||
// this.canvas.renderAll();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -958,6 +958,9 @@ export class CanvasManager {
|
||||
// 解析JSON字符串
|
||||
try {
|
||||
const parsedJson = JSON.parse(json);
|
||||
this.canvasWidth.value = parsedJson.canvasWidth || this.width;
|
||||
this.canvasHeight.value = parsedJson.canvasHeight || this.height;
|
||||
this.canvasColor.value = parsedJson.canvasColor || this.backgroundColor;
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const tempLayers = JSON.parse(parsedJson?.layers) || [];
|
||||
@@ -974,6 +977,7 @@ export class CanvasManager {
|
||||
}
|
||||
|
||||
this.layers.value = tempLayers;
|
||||
debugger;
|
||||
|
||||
// this.canvasWidth.value = parsedJson.canvasWidth || this.width;
|
||||
// this.canvasHeight.value = parsedJson.canvasHeight || this.height;
|
||||
|
||||
@@ -67,6 +67,8 @@ import {
|
||||
} from "../utils/helper";
|
||||
import { message } from "ant-design-vue";
|
||||
import { fabric } from "fabric-with-all";
|
||||
import { getOriginObjectInfo } from "../utils/layerUtils";
|
||||
import { restoreFabricObject } from "../utils/objectHelper";
|
||||
|
||||
/**
|
||||
* 图层管理器 - 负责管理画布上的所有图层
|
||||
@@ -333,52 +335,94 @@ export class LayerManager {
|
||||
});
|
||||
|
||||
// 设置裁剪对象
|
||||
layers.forEach((layer) => {
|
||||
layers.forEach(async (layer) => {
|
||||
if (layer.clippingMask) {
|
||||
const activeObject = this.canvas.getActiveObject();
|
||||
if (activeObject?._objects?.length > 1) {
|
||||
console.log(activeObject?._objects?.length);
|
||||
return false; // 如果是多选对象,则不设置裁剪路径
|
||||
}
|
||||
// 反序列化 clippingMask
|
||||
const clippingMaskFabricObject = await restoreFabricObject(
|
||||
layer.clippingMask,
|
||||
this.canvas
|
||||
);
|
||||
clippingMaskFabricObject.clipPath = null;
|
||||
|
||||
clippingMaskFabricObject.set({
|
||||
// 设置绝对定位
|
||||
// ...getOriginObjectInfo(layer.clippingMask), // 恢复原定位
|
||||
absolutePositioned: true,
|
||||
});
|
||||
|
||||
// const activeObject = this.canvas.getActiveObject();
|
||||
// if (activeObject?._objects?.length > 1) {
|
||||
// const { object } = findObjectById(
|
||||
// this.canvas,
|
||||
// layer.clippingMask?.id
|
||||
// );
|
||||
// if (!object) return;
|
||||
// const tempClipPath = fabric.util.object.clone(object);
|
||||
// tempClipPath.clipPath = null;
|
||||
// tempClipPath.set({
|
||||
// // 设置绝对定位
|
||||
// ...getOriginObjectInfo(layer.clippingMask), // 恢复原定位
|
||||
// absolutePositioned: true,
|
||||
// });
|
||||
// activeObject.clipPath = tempClipPath;
|
||||
// // 确保选择组正确渲染
|
||||
// // activeObject.setCoords();
|
||||
// console.log(activeObject?._objects?.length);
|
||||
// return; // 如果是多选对象,则不设置裁剪路径
|
||||
// }
|
||||
// 如果是组图层 则给所有子对象设置裁剪对象
|
||||
if (layer.type === LayerType.GROUP || layer.children?.length > 0) {
|
||||
layer.children.forEach((childLayer) => {
|
||||
const { object } = findObjectById(
|
||||
this.canvas,
|
||||
layer.clippingMask?.id
|
||||
);
|
||||
if (object) {
|
||||
const tempClipPath = fabric.util.object.clone(object);
|
||||
tempClipPath.clipPath = null;
|
||||
tempClipPath.set({
|
||||
// 设置绝对定位
|
||||
// ...layer.clippingMask, // 恢复原定位
|
||||
absolutePositioned: true,
|
||||
});
|
||||
if (clippingMaskFabricObject) {
|
||||
const childObj = this.canvas
|
||||
.getObjects()
|
||||
.find((o) => o.layerId === childLayer.id);
|
||||
if (childObj) {
|
||||
childObj.clipPath = tempClipPath;
|
||||
childObj.clipPath = clippingMaskFabricObject;
|
||||
}
|
||||
}
|
||||
// const { object } = findObjectById(
|
||||
// this.canvas,
|
||||
// layer.clippingMask?.id
|
||||
// );
|
||||
// if (object) {
|
||||
// const tempClipPath = fabric.util.object.clone(object);
|
||||
// tempClipPath.clipPath = null;
|
||||
// tempClipPath.set({
|
||||
// // 设置绝对定位
|
||||
// // ...layer.clippingMask, // 恢复原定位
|
||||
// ...getOriginObjectInfo(layer.clippingMask),
|
||||
// absolutePositioned: true,
|
||||
// });
|
||||
// const childObj = this.canvas
|
||||
// .getObjects()
|
||||
// .find((o) => o.layerId === childLayer.id);
|
||||
// if (childObj) {
|
||||
// childObj.clipPath = tempClipPath;
|
||||
// }
|
||||
// }
|
||||
});
|
||||
} else {
|
||||
const { object } = findObjectById(
|
||||
this.canvas,
|
||||
layer.clippingMask?.id
|
||||
);
|
||||
if (object) {
|
||||
const tempClipPath = fabric.util.object.clone(object);
|
||||
tempClipPath.clipPath = null; // 确保克隆的遮罩没有clipPath
|
||||
tempClipPath.set({
|
||||
// 设置绝对定位
|
||||
// ...layer.clippingMask, // 恢复原定位
|
||||
absolutePositioned: true,
|
||||
});
|
||||
obj.clipPath = tempClipPath;
|
||||
}
|
||||
} else if (clippingMaskFabricObject) {
|
||||
obj.clipPath = clippingMaskFabricObject;
|
||||
}
|
||||
|
||||
// {
|
||||
// // const { object } = findObjectById(
|
||||
// // this.canvas,
|
||||
// // layer.clippingMask?.id
|
||||
// // );
|
||||
// // if (object) {
|
||||
// // const tempClipPath = fabric.util.object.clone(object);
|
||||
// // tempClipPath.clipPath = null; // 确保克隆的遮罩没有clipPath
|
||||
// // tempClipPath.set({
|
||||
// // // 设置绝对定位
|
||||
// // // ...layer.clippingMask, // 恢复原定位
|
||||
// // ...getOriginObjectInfo(layer.clippingMask),
|
||||
// // absolutePositioned: true,
|
||||
// // });
|
||||
// // obj.clipPath = tempClipPath;
|
||||
// // }
|
||||
// }
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -932,17 +976,16 @@ export class LayerManager {
|
||||
// 设置激活当前图层下画布中的所有对象,并变成选择组
|
||||
setAllActiveGroupLayerCanvasObject(layer) {
|
||||
// 获取当前图层下所有元素
|
||||
let layerMask = null;
|
||||
// 选择当前组下所有画布元素
|
||||
const allObjects = layer.children.reduce((acc, child) => {
|
||||
// 如果子图层有fabricObjects,则添加到结果数组
|
||||
child?.fabricObjects?.forEach((obj) => {
|
||||
const { object } = findObjectById(this.canvas, obj.id);
|
||||
if (object) {
|
||||
if (!layerMask) {
|
||||
layerMask = fabric.util.object.clone(object.clipPath);
|
||||
}
|
||||
object.clipPath = null; // 确保克隆的遮罩没有clipPath
|
||||
// if (!layerMask) {
|
||||
// layerMask = fabric.util.object.clone(object.clipPath);
|
||||
// }
|
||||
// object.clipPath = null; // 确保克隆的遮罩没有clipPath
|
||||
acc.push(object);
|
||||
}
|
||||
});
|
||||
@@ -950,10 +993,10 @@ export class LayerManager {
|
||||
if (child?.fabricObject) {
|
||||
const { object } = findObjectById(this.canvas, child?.fabricObject.id);
|
||||
if (object) {
|
||||
if (!layerMask) {
|
||||
layerMask = fabric.util.object.clone(object.clipPath);
|
||||
}
|
||||
object.clipPath = null; // 确保克隆的遮罩没有clipPath
|
||||
// if (!layerMask) {
|
||||
// layerMask = fabric.util.object.clone(object.clipPath);
|
||||
// }
|
||||
// object.clipPath = null; // 确保克隆的遮罩没有clipPath
|
||||
acc.push(object);
|
||||
}
|
||||
}
|
||||
@@ -967,17 +1010,48 @@ export class LayerManager {
|
||||
|
||||
// 如果有对象,创建选择组
|
||||
this.canvas.discardActiveObject(); // 取消当前活动对象
|
||||
this.canvas.renderAll(); // 确保画布渲染
|
||||
|
||||
// const { object } = findObjectById(this.canvas, layer.clippingMask?.id);
|
||||
|
||||
// 选中多个对象,不是创建组
|
||||
// 多个对象时创建活动选择组
|
||||
const activeSelection = new fabric.ActiveSelection(allObjects, {
|
||||
let activeSelection = new fabric.ActiveSelection(allObjects, {
|
||||
canvas: this.canvas,
|
||||
});
|
||||
|
||||
activeSelection.clipPath = layerMask; // 保留第一个对象的裁剪路径
|
||||
// if (object) {
|
||||
// const tempClipPath = fabric.util.object.clone(object);
|
||||
// tempClipPath.clipPath = null;
|
||||
// tempClipPath.set({
|
||||
// // 设置绝对定位
|
||||
// // ...layer.clippingMask, // 恢复原定位
|
||||
// ...getOriginObjectInfo(layer.clippingMask),
|
||||
// absolutePositioned: true,
|
||||
// });
|
||||
// activeSelection.clipPath = tempClipPath; // 保留第一个对象的裁剪路径
|
||||
// }
|
||||
|
||||
// // 监听选择取消事件,恢复原始裁剪路径
|
||||
// const restoreClipPaths = () => {
|
||||
// allObjects.forEach((obj) => {
|
||||
// if (obj._originalClipPath !== undefined) {
|
||||
// obj.clipPath = obj._originalClipPath;
|
||||
// delete obj._originalClipPath;
|
||||
// }
|
||||
// });
|
||||
// this.canvas.off("selection:cleared", restoreClipPaths);
|
||||
// this.canvas.off("selection:updated", restoreClipPaths);
|
||||
// };
|
||||
|
||||
// this.canvas.on("selection:cleared", restoreClipPaths);
|
||||
// this.canvas.on("selection:updated", restoreClipPaths);
|
||||
|
||||
// 设置活动选择组的属性
|
||||
this.canvas.setActiveObject(activeSelection);
|
||||
this.canvas.renderAll();
|
||||
activeSelection = null; // 清理引用,避免内存泄漏
|
||||
// 确保选择组正确渲染
|
||||
// activeSelection.setCoords();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { generateId } from "./helper";
|
||||
|
||||
/**
|
||||
* 图层类型枚举
|
||||
*/
|
||||
|
||||
@@ -20,11 +20,13 @@ export function buildLayerAssociations(layer, canvasObjects) {
|
||||
}
|
||||
|
||||
if (layer.clippingMask) {
|
||||
// clippingMask 可能是一个fabricObject或组
|
||||
// clippingMask 可能是一个fabricObject或组 也可能是一个简单对象
|
||||
const clippingMaskObj = canvasObjects.find(
|
||||
(obj) => obj.id === layer.clippingMask.id
|
||||
);
|
||||
layer.clippingMask = clippingMaskObj?.toObject?.(["id"]) || null;
|
||||
layer.clippingMask = clippingMaskObj
|
||||
? clippingMaskObj?.toObject?.(["id"])
|
||||
: layer.clippingMask;
|
||||
}
|
||||
|
||||
// 处理多个fabricObjects关联
|
||||
@@ -170,12 +172,15 @@ export function simplifyLayers(layers) {
|
||||
opacity: layer.opacity,
|
||||
isBackground: layer.isBackground || false,
|
||||
isFixed: layer.isFixed || false,
|
||||
clippingMask: layer.clippingMask
|
||||
? {
|
||||
id: layer.clippingMask.id,
|
||||
type: layer.clippingMask.type,
|
||||
}
|
||||
: null,
|
||||
clippingMask:
|
||||
layer.clippingMask?.toObject?.(["id", "layerId"]) ||
|
||||
layer.clippingMask ||
|
||||
null, // 可能是一个fabricObject或组 也可能是一个简单对象
|
||||
// ? {
|
||||
// id: layer.clippingMask.id,
|
||||
// type: layer.clippingMask.type,
|
||||
// }
|
||||
// : null,
|
||||
fabricObject: layer.fabricObject
|
||||
? {
|
||||
id: layer.fabricObject.id,
|
||||
@@ -226,11 +231,21 @@ export function restoreLayers(simplifiedLayers, canvasObjects) {
|
||||
const restoredLayer = { ...layer };
|
||||
|
||||
// 恢复clippingMask关联
|
||||
if (layer.clippingMask?.id) {
|
||||
// if (layer.clippingMask?.id) {
|
||||
// const clippingMaskObj = canvasObjects.find(
|
||||
// (obj) => obj.id === layer.clippingMask.id
|
||||
// );
|
||||
// restoredLayer.clippingMask = clippingMaskObj || null;
|
||||
// }
|
||||
|
||||
if (layer.clippingMask) {
|
||||
// clippingMask 可能是一个fabricObject或组 也可能是一个简单对象
|
||||
const clippingMaskObj = canvasObjects.find(
|
||||
(obj) => obj.id === layer.clippingMask.id
|
||||
);
|
||||
restoredLayer.clippingMask = clippingMaskObj || null;
|
||||
restoredLayer.clippingMask = clippingMaskObj
|
||||
? clippingMaskObj?.toObject?.(["id"])
|
||||
: layer.clippingMask;
|
||||
}
|
||||
|
||||
// 恢复单个fabricObject关联
|
||||
@@ -334,3 +349,30 @@ export function restoreFromSnapshot(snapshot, canvasObjects) {
|
||||
|
||||
return restoreLayers(snapshot.data, canvasObjects);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象的定位信息
|
||||
* @param {Object} object 对象
|
||||
* @returns {Object} 对象的定位信息
|
||||
*/
|
||||
export function getOriginObjectInfo(object) {
|
||||
if (!object) {
|
||||
console.warn("getOriginObjectInfo 请传入有效的fabric对象:", object);
|
||||
return {};
|
||||
}
|
||||
|
||||
// 获取对象的原始信息
|
||||
const originInfo = {
|
||||
left: object.left || 0,
|
||||
top: object.top || 0,
|
||||
scaleX: object.scaleX || 1,
|
||||
scaleY: object.scaleY || 1,
|
||||
angle: object.angle || 0,
|
||||
flipX: object.flipX || false,
|
||||
flipY: object.flipY || false,
|
||||
skewX: object.skewX || 0,
|
||||
skewY: object.skewY || 0,
|
||||
};
|
||||
|
||||
return originInfo;
|
||||
}
|
||||
|
||||
63
src/component/Canvas/CanvasEditor/utils/objectHelper.js
Normal file
63
src/component/Canvas/CanvasEditor/utils/objectHelper.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { fabric } from "fabric-with-all";
|
||||
|
||||
/**
|
||||
* 将序列化对象恢复为 fabric 对象
|
||||
* @param {Object} serializedObject - toObject() 生成的对象
|
||||
* @param {fabric.Canvas} canvas - 目标画布
|
||||
* @returns {Promise<fabric.Object>} 恢复的 fabric 对象
|
||||
*/
|
||||
export async function restoreFabricObject(serializedObject, canvas) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const objectType = serializedObject.type;
|
||||
// 定义恢复后的处理函数
|
||||
const handleRestoredObject = (fabricObject) => {
|
||||
if (!fabricObject) {
|
||||
reject(new Error(`无法恢复 ${objectType} 类型的对象`));
|
||||
return;
|
||||
}
|
||||
|
||||
// 恢复自定义属性
|
||||
if (serializedObject.id) fabricObject.id = serializedObject.id;
|
||||
if (serializedObject.layerId)
|
||||
fabricObject.layerId = serializedObject.layerId;
|
||||
if (serializedObject.layerName)
|
||||
fabricObject.layerName = serializedObject.layerName;
|
||||
|
||||
// 更新坐标
|
||||
fabricObject.setCoords();
|
||||
|
||||
// 添加到画布
|
||||
// canvas.add(fabricObject);
|
||||
|
||||
resolve(fabricObject);
|
||||
};
|
||||
|
||||
// 根据类型选择恢复方法
|
||||
switch (objectType) {
|
||||
case "rect":
|
||||
fabric.Rect.fromObject(serializedObject, handleRestoredObject);
|
||||
break;
|
||||
case "circle":
|
||||
fabric.Circle.fromObject(serializedObject, handleRestoredObject);
|
||||
break;
|
||||
case "path":
|
||||
fabric.Path.fromObject(serializedObject, handleRestoredObject);
|
||||
break;
|
||||
case "image":
|
||||
fabric.Image.fromObject(serializedObject, handleRestoredObject);
|
||||
break;
|
||||
case "group":
|
||||
fabric.Group.fromObject(serializedObject, handleRestoredObject);
|
||||
break;
|
||||
default:
|
||||
// 使用通用方法
|
||||
fabric.util.enlivenObjects([serializedObject], (objects) => {
|
||||
if (objects && objects[0]) {
|
||||
handleRestoredObject(objects[0]);
|
||||
} else {
|
||||
reject(new Error("对象恢复失败"));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user