Files
aida_front/src/component/Canvas/CanvasEditor/commands/ObjectLayerCommands.js

943 lines
24 KiB
JavaScript
Raw Normal View History

2025-06-09 10:25:54 +08:00
import { OperationType } from "../utils/layerHelper";
import { Command } from "./Command";
import { generateId } from "../utils/helper";
/**
* 设置活动图层命令
*/
export class SetActiveLayerCommand extends Command {
constructor(options) {
super({
name: "设置活动图层",
saveState: false,
});
this.layers = options.layers;
this.canvas = options.canvas;
this.activeLayerId = options.activeLayerId;
this.layerId = options.layerId;
this.oldActiveLayerId = this.activeLayerId.value;
this.layerManager = options.layerManager;
this.oldActiveObjects = [];
this.newLayer = null;
this.editorMode = options.editorMode;
}
execute() {
this.newLayer = this.layers.value.find(
(layer) => layer.id === this.layerId
);
if (!this.newLayer) {
console.error(`图层 ${this.layerId} 不存在`);
return false;
}
// 如果是背景层,不设置为活动图层
if (this.newLayer.isBackground) {
console.warn("背景层不能设为活动图层");
return false;
}
// 如果图层已锁定,不设置为活动图层
if (this.newLayer.locked) {
console.warn("锁定图层不能设为活动图层");
return false;
}
this.oldActiveObjects = this.canvas.getActiveObjects();
// 设置为活动图层
this.activeLayerId.value = this.layerId;
// 如果在选择模式下,取消所有选择
if (this.editorMode === OperationType.SELECT && this.canvas) {
this.canvas.discardActiveObject();
// 设置为新的图层下的对象为激活,但需要确保对象存在于画布上
if (
this.newLayer.fabricObjects &&
this.newLayer.fabricObjects.length > 0
) {
const canvasObjects = this.canvas.getObjects();
const validObjects = this.newLayer.fabricObjects.filter(
(obj) => obj && canvasObjects.includes(obj)
);
if (validObjects.length > 0) {
if (validObjects.length === 1) {
// 只有一个对象时直接设置
this.canvas.setActiveObject(validObjects[0]);
} else {
// 多个对象时创建活动选择组
const activeSelection = new fabric.ActiveSelection(validObjects, {
canvas: this.canvas,
});
this.canvas.setActiveObject(activeSelection);
}
}
}
this.canvas.renderAll();
}
return true;
}
undo() {
// 恢复原活动图层ID
this.activeLayerId.value = this.oldActiveLayerId;
// 如果在选择模式下,恢复取消的所有选择
if (this.editorMode === OperationType.SELECT && this.canvas) {
this.canvas.discardActiveObject();
// 修复:确保对象存在于画布上才激活
if (this.oldActiveObjects && this.oldActiveObjects.length > 0) {
const canvasObjects = this.canvas.getObjects();
const validObjects = this.oldActiveObjects.filter(
(obj) => obj && canvasObjects.includes(obj)
);
if (validObjects.length > 0) {
if (validObjects.length > 1) {
// 如果有多个对象,需要创建一个活动选择组
const activeSelection = new fabric.ActiveSelection(validObjects, {
canvas: this.canvas,
});
this.canvas.setActiveObject(activeSelection);
} else {
// 只有一个对象时直接设置
this.canvas.setActiveObject(validObjects[0]);
}
}
}
this.canvas.renderAll();
}
}
getInfo() {
return {
name: this.name,
layerId: this.layerId,
oldActiveLayerId: this.oldActiveLayerId,
};
}
}
/**
* 添加对象到图层命令
*/
export class AddObjectToLayerCommand extends Command {
constructor(options) {
super({
name: "添加对象到图层",
saveState: true,
});
this.canvas = options.canvas;
this.layers = options.layers;
this.layerId = options.layerId;
this.fabricObject = options.fabricObject;
// 保存对象原始状态和ID
this.objectId =
this.fabricObject.id ||
`obj_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
this.originalObjectState = this.fabricObject.toObject([
"id",
"layerId",
"layerName",
]);
}
execute() {
// 查找目标图层
const layer = this.layers.value.find((l) => l.id === this.layerId);
if (!layer) {
console.error(`图层 ${this.layerId} 不存在`);
return null;
}
// 如果是背景层,不允许添加对象
if (layer.isBackground) {
console.warn("不能向背景层添加对象");
return null;
}
// 为对象生成唯一ID
this.fabricObject.id = this.objectId;
// 设置对象与图层的关联
this.fabricObject.layerId = this.layerId;
this.fabricObject.layerName = layer.name;
// 设置对象可操作可选择
this.fabricObject.selectable = true;
this.fabricObject.evented = true;
// 将对象添加到画布
this.canvas.add(this.fabricObject);
// 将对象添加到图层的fabricObjects数组
layer.fabricObjects = layer.fabricObjects || [];
layer.fabricObjects.push(this.fabricObject);
this.canvas.discardActiveObject();
// 确保对象确实存在于画布上才激活
const canvasObjects = this.canvas.getObjects();
const validObjects = layer.fabricObjects.filter(
(obj) => obj && canvasObjects.includes(obj)
);
if (validObjects.length > 0) {
if (validObjects.length === 1) {
// 只有一个对象时直接设置
this.canvas.setActiveObject(validObjects[0]);
} else {
// 多个对象时创建活动选择组
const activeSelection = new fabric.ActiveSelection(validObjects, {
canvas: this.canvas,
});
this.canvas.setActiveObject(activeSelection);
}
}
// 更新画布
this.canvas.renderAll();
return this.fabricObject;
}
undo() {
// 查找图层
const layer = this.layers.value.find((l) => l.id === this.layerId);
if (!layer) {
return false;
}
// 从图层的fabricObjects数组中移除对象
if (layer.fabricObjects) {
layer.fabricObjects = layer.fabricObjects.filter(
(obj) => obj.id !== this.objectId
);
}
// 从画布移除对象
const object = this.canvas
.getObjects()
.find((obj) => obj.id === this.objectId);
if (object) {
// 先丢弃活动对象,避免控制点渲染错误
this.canvas.discardActiveObject();
this.canvas.remove(object);
// 更新画布
this.canvas.renderAll();
}
return true;
}
getInfo() {
return {
name: this.name,
layerId: this.layerId,
objectId: this.objectId,
};
}
}
/**
* 从图层中移除对象命令
*/
export class RemoveObjectFromLayerCommand extends Command {
constructor(options) {
super({
name: "从图层中移除对象",
saveState: true,
});
this.canvas = options.canvas;
this.layers = options.layers;
this.objectId = options.objectId;
// 查找对象和图层
this.object =
typeof options.objectOrId === "object"
? options.objectOrId
: this.canvas.getObjects().find((obj) => obj.id === this.objectId);
if (this.object) {
this.layerId = this.object.layerId;
this.objectData = this.object.toObject(["id", "layerId", "layerName"]);
}
}
execute() {
if (!this.object) {
console.error(`对象 ${this.objectId} 不存在`);
return false;
}
if (!this.layerId) {
console.error(`对象 ${this.objectId} 未关联到任何图层`);
return false;
}
// 查找图层
const layer = this.layers.value.find((l) => l.id === this.layerId);
if (!layer) {
console.error(`图层 ${this.layerId} 不存在`);
return false;
}
// 从画布移除对象
this.canvas.remove(this.object);
// 从图层的fabricObjects数组移除对象
if (layer.fabricObjects) {
layer.fabricObjects = layer.fabricObjects.filter(
(obj) => obj.id !== this.objectId
);
}
// 更新画布
this.canvas.renderAll();
return true;
}
undo() {
if (!this.objectData || !this.layerId) {
return false;
}
// 查找图层
const layer = this.layers.value.find((l) => l.id === this.layerId);
if (!layer) {
return false;
}
// 恢复对象到画布
fabric.util.enlivenObjects([this.objectData], (objects) => {
const restoredObject = objects[0];
// 将对象添加到画布
this.canvas.add(restoredObject);
// 将对象添加回图层
layer.fabricObjects = layer.fabricObjects || [];
layer.fabricObjects.push(restoredObject);
// 更新画布
this.canvas.renderAll();
});
return true;
}
getInfo() {
return {
name: this.name,
objectId: this.objectId,
layerId: this.layerId,
};
}
}
/**
* 更换固定图层图像命令
* 专门用于更换固定图层如背景图层的图像
*/
export class ChangeFixedImageCommand extends Command {
constructor(options = {}) {
super();
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.imageUrl = options.imageUrl;
this.targetLayerType = options.targetLayerType || "background"; // 'background', 'fixed', etc.
this.position = options.position || { x: 0, y: 0 };
this.scale = options.scale || { x: 1, y: 1 };
this.preserveTransform = options.preserveTransform !== false; // 默认保留变换
// 用于回滚的状态
this.previousImage = null;
this.previousTransform = null;
this.targetLayer = null;
this.isExecuted = false;
// 错误处理
this.maxRetries = options.maxRetries || 3;
this.retryCount = 0;
this.timeoutMs = options.timeoutMs || 10000;
}
async execute() {
try {
this.validateInputs();
// 查找目标图层
this.targetLayer = this.findTargetLayer();
if (!this.targetLayer) {
throw new Error(`找不到目标图层类型: ${this.targetLayerType}`);
}
// 保存当前状态用于回滚
await this.saveCurrentState();
// 加载新图像
const newImage = await this.loadImageWithRetry();
// 应用图像到图层
await this.applyImageToLayer(newImage);
this.isExecuted = true;
// 触发成功事件
this.emitEvent("image:changed", {
layerId: this.targetLayer.id,
newImageUrl: this.imageUrl,
command: this,
});
return {
success: true,
layerId: this.targetLayer.id,
imageUrl: this.imageUrl,
};
} catch (error) {
console.error("ChangeFixedImageCommand执行失败:", error);
// 如果已经执行了部分操作,尝试回滚
if (this.isExecuted) {
try {
await this.undo();
} catch (rollbackError) {
console.error("回滚失败:", rollbackError);
}
}
throw error;
}
}
async undo() {
if (!this.isExecuted || !this.targetLayer) {
throw new Error("命令未执行或目标图层不存在");
}
try {
if (this.previousImage) {
// 恢复之前的图像
await this.restorePreviousImage();
} else {
// 如果没有之前的图像,移除当前图像
await this.removeCurrentImage();
}
this.isExecuted = false;
// 触发撤销事件
this.emitEvent("image:reverted", {
layerId: this.targetLayer.id,
command: this,
});
return {
success: true,
action: "reverted",
layerId: this.targetLayer.id,
};
} catch (error) {
console.error("ChangeFixedImageCommand撤销失败:", error);
throw error;
}
}
validateInputs() {
if (!this.canvas) throw new Error("Canvas实例是必需的");
if (!this.layerManager) throw new Error("LayerManager实例是必需的");
if (!this.imageUrl) throw new Error("图像URL是必需的");
// 验证URL格式
try {
new URL(this.imageUrl);
} catch {
throw new Error("无效的图像URL格式");
}
}
findTargetLayer() {
const layers = this.layerManager.layers?.value || [];
switch (this.targetLayerType) {
case "background":
return layers.find((layer) => layer.isBackground);
case "fixed":
return layers.find((layer) => layer.isFixed);
default:
return layers.find((layer) => layer.type === this.targetLayerType);
}
}
async saveCurrentState() {
if (!this.targetLayer.fabricObject) return;
const currentObj = this.targetLayer.fabricObject;
// 保存当前图像URL如果存在
this.previousImage = {
url: currentObj.getSrc ? currentObj.getSrc() : null,
element: currentObj._element ? currentObj._element.cloneNode() : null,
};
// 保存变换状态
this.previousTransform = {
left: currentObj.left,
top: currentObj.top,
scaleX: currentObj.scaleX,
scaleY: currentObj.scaleY,
angle: currentObj.angle,
flipX: currentObj.flipX,
flipY: currentObj.flipY,
opacity: currentObj.opacity,
};
}
async loadImageWithRetry() {
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
return await this.loadImage();
} catch (error) {
this.retryCount = attempt;
if (attempt === this.maxRetries) {
throw new Error(
`图像加载失败,已重试${this.maxRetries}次: ${error.message}`
);
}
// 指数退避重试
const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
console.warn(
`图像加载重试 ${attempt + 1}/${this.maxRetries}:`,
error.message
);
}
}
}
loadImage() {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(
new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)
);
}, this.timeoutMs);
fabric.Image.fromURL(
this.imageUrl,
(img) => {
clearTimeout(timeout);
if (!img || !img.getElement()) {
reject(new Error("图像加载失败或无效"));
return;
}
resolve(img);
},
{
crossOrigin: "anonymous",
}
);
});
}
async applyImageToLayer(newImage) {
const currentObj = this.targetLayer.fabricObject;
// 设置基本属性
newImage.set({
id: currentObj?.id || generateId(),
layerId: this.targetLayer.id,
layerName: this.targetLayer.name,
isBackground: this.targetLayer.isBackground,
isFixed: this.targetLayer.isFixed,
});
// 应用位置和变换
if (this.preserveTransform && this.previousTransform) {
newImage.set(this.previousTransform);
} else {
newImage.set({
left: this.position.x,
top: this.position.y,
scaleX: this.scale.x,
scaleY: this.scale.y,
});
}
// 移除旧对象(如果存在)
if (currentObj) {
this.canvas.remove(currentObj);
}
// 添加新图像
this.canvas.add(newImage);
newImage.setCoords();
// 更新图层引用
this.targetLayer.fabricObject = newImage;
// 更新图层管理器
this.layerManager.updateLayerObject(this.targetLayer.id, newImage);
// 重新渲染画布
this.canvas.renderAll();
}
async restorePreviousImage() {
if (!this.previousImage.url) return;
const restoredImage = await this.loadImageFromUrl(this.previousImage.url);
// 恢复之前的变换
if (this.previousTransform) {
restoredImage.set(this.previousTransform);
}
// 设置图层属性
restoredImage.set({
id: this.targetLayer.fabricObject?.id || generateId(),
layerId: this.targetLayer.id,
layerName: this.targetLayer.name,
isBackground: this.targetLayer.isBackground,
isFixed: this.targetLayer.isFixed,
});
// 替换当前对象
if (this.targetLayer.fabricObject) {
this.canvas.remove(this.targetLayer.fabricObject);
}
this.canvas.add(restoredImage);
restoredImage.setCoords();
// 更新引用
this.targetLayer.fabricObject = restoredImage;
this.layerManager.updateLayerObject(this.targetLayer.id, restoredImage);
this.canvas.renderAll();
}
async removeCurrentImage() {
if (this.targetLayer.fabricObject) {
this.canvas.remove(this.targetLayer.fabricObject);
this.targetLayer.fabricObject = null;
this.layerManager.updateLayerObject(this.targetLayer.id, null);
this.canvas.renderAll();
}
}
loadImageFromUrl(url) {
return new Promise((resolve, reject) => {
fabric.Image.fromURL(
url,
(img) => {
if (!img || !img.getElement()) {
reject(new Error("恢复图像加载失败"));
return;
}
resolve(img);
},
{ crossOrigin: "anonymous" }
);
});
}
emitEvent(eventName, data) {
if (this.canvas && this.canvas.fire) {
this.canvas.fire(eventName, data);
}
}
// 获取命令信息用于调试
getCommandInfo() {
return {
type: "ChangeFixedImageCommand",
targetLayerType: this.targetLayerType,
imageUrl: this.imageUrl,
isExecuted: this.isExecuted,
retryCount: this.retryCount,
targetLayerId: this.targetLayer?.id,
preserveTransform: this.preserveTransform,
};
}
}
/**
* 向图层添加图像命令
* 用于向指定图层添加新的图像对象
*/
export class AddImageToLayerCommand extends Command {
constructor(options = {}) {
super();
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.imageUrl = options.imageUrl;
this.layerId = options.layerId;
this.position = options.position || { x: 100, y: 100 };
this.scale = options.scale || { x: 1, y: 1 };
this.zIndex = options.zIndex || null; // 可选的层级控制
// 用于回滚的状态
this.addedObject = null;
this.targetLayer = null;
this.isExecuted = false;
// 错误处理
this.maxRetries = options.maxRetries || 3;
this.retryCount = 0;
this.timeoutMs = options.timeoutMs || 10000;
}
async execute() {
try {
this.validateInputs();
// 查找目标图层
this.targetLayer = this.findTargetLayer();
if (!this.targetLayer) {
throw new Error(`找不到目标图层: ${this.layerId}`);
}
// 检查图层是否可编辑
this.validateLayerEditability();
// 加载新图像
const newImage = await this.loadImageWithRetry();
// 添加图像到图层
await this.addImageToLayer(newImage);
this.isExecuted = true;
// 触发成功事件
this.emitEvent("image:added", {
layerId: this.layerId,
objectId: this.addedObject.id,
imageUrl: this.imageUrl,
command: this,
});
return {
success: true,
layerId: this.layerId,
objectId: this.addedObject.id,
imageUrl: this.imageUrl,
};
} catch (error) {
console.error("AddImageToLayerCommand执行失败:", error);
// 如果已经添加了对象,尝试移除
if (this.addedObject) {
try {
await this.undo();
} catch (rollbackError) {
console.error("回滚失败:", rollbackError);
}
}
throw error;
}
}
async undo() {
if (!this.isExecuted || !this.addedObject) {
throw new Error("命令未执行或没有添加的对象");
}
try {
// 移除添加的对象
this.canvas.remove(this.addedObject);
// 从图层管理器中移除
this.layerManager.removeObjectFromLayer(
this.addedObject.id,
this.layerId
);
this.isExecuted = false;
// 触发撤销事件
this.emitEvent("image:removed", {
layerId: this.layerId,
objectId: this.addedObject.id,
command: this,
});
// 重新渲染
this.canvas.renderAll();
return {
success: true,
action: "removed",
layerId: this.layerId,
objectId: this.addedObject.id,
};
} catch (error) {
console.error("AddImageToLayerCommand撤销失败:", error);
throw error;
}
}
validateInputs() {
if (!this.canvas) throw new Error("Canvas实例是必需的");
if (!this.layerManager) throw new Error("LayerManager实例是必需的");
if (!this.imageUrl) throw new Error("图像URL是必需的");
if (!this.layerId) throw new Error("图层ID是必需的");
// 验证URL格式
try {
new URL(this.imageUrl);
} catch {
throw new Error("无效的图像URL格式");
}
}
findTargetLayer() {
const layers = this.layerManager.layers?.value || [];
return layers.find((layer) => layer.id === this.layerId);
}
validateLayerEditability() {
if (this.targetLayer.locked) {
throw new Error("目标图层已锁定,无法添加对象");
}
if (!this.targetLayer.visible) {
console.warn("目标图层不可见,添加的对象可能不会显示");
}
}
async loadImageWithRetry() {
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
return await this.loadImage();
} catch (error) {
this.retryCount = attempt;
if (attempt === this.maxRetries) {
throw new Error(
`图像加载失败,已重试${this.maxRetries}次: ${error.message}`
);
}
// 指数退避重试
const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
console.warn(
`图像加载重试 ${attempt + 1}/${this.maxRetries}:`,
error.message
);
}
}
}
loadImage() {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(
new Error(`图像加载超时 (${this.timeoutMs}ms): ${this.imageUrl}`)
);
}, this.timeoutMs);
fabric.Image.fromURL(
this.imageUrl,
(img) => {
clearTimeout(timeout);
if (!img || !img.getElement()) {
reject(new Error("图像加载失败或无效"));
return;
}
resolve(img);
},
{
crossOrigin: "anonymous",
}
);
});
}
async addImageToLayer(newImage) {
// 生成唯一ID
const objectId = generateId();
// 设置图像属性
newImage.set({
id: objectId,
layerId: this.layerId,
layerName: this.targetLayer.name,
left: this.position.x,
top: this.position.y,
scaleX: this.scale.x,
scaleY: this.scale.y,
selectable: true,
evented: true,
});
// 添加到画布
this.canvas.add(newImage);
// 设置层级
if (this.zIndex !== null) {
this.setObjectZIndex(newImage, this.zIndex);
}
newImage.setCoords();
// 保存引用用于回滚
this.addedObject = newImage;
// 添加到图层管理器
this.layerManager.addObjectToLayer(newImage, this.layerId);
// 重新渲染画布
this.canvas.renderAll();
}
setObjectZIndex(object, zIndex) {
if (zIndex === "top") {
object.bringToFront();
} else if (zIndex === "bottom") {
object.sendToBack();
} else if (typeof zIndex === "number") {
object.moveTo(zIndex);
}
}
emitEvent(eventName, data) {
if (this.canvas && this.canvas.fire) {
this.canvas.fire(eventName, data);
}
}
// 获取命令信息用于调试
getCommandInfo() {
return {
type: "AddImageToLayerCommand",
layerId: this.layerId,
imageUrl: this.imageUrl,
position: this.position,
scale: this.scale,
isExecuted: this.isExecuted,
retryCount: this.retryCount,
addedObjectId: this.addedObject?.id,
};
}
}