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

904 lines
24 KiB
JavaScript
Raw Normal View History

2025-06-09 10:25:54 +08:00
import { Command, FunctionCommand } from "./Command";
2025-06-18 11:05:23 +08:00
import { getLiquifyReferenceManager } from "../managers/LiquifyReferenceManager";
2025-06-09 10:25:54 +08:00
/**
* 液化命令基类
* 所有液化相关命令的基类
*/
export class LiquifyCommand extends Command {
/**
* 创建液化命令
* @param {Object} options 配置选项
* @param {Object} options.canvas Fabric.js画布实例
* @param {Object} options.layerManager 图层管理器实例
* @param {Object} options.liquifyManager 液化管理器实例
* @param {String} options.mode 液化模式
* @param {Object} options.params 液化参数
* @param {Object} options.targetObject 目标对象
* @param {ImageData} options.originalData 原始图像数据
* @param {ImageData} options.resultData 变形后图像数据
*/
constructor(options) {
super({
name: options.name || `液化操作: ${options.mode || "未知模式"}`,
description:
options.description ||
`使用${options.mode || "未知模式"}模式进行液化操作`,
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.liquifyManager = options.liquifyManager;
this.mode = options.mode;
this.params = options.params || {};
this.targetObject = options.targetObject;
this.targetLayerId = options.targetLayerId;
this.originalData = options.originalData; // 操作前的图像数据
this.resultData = options.resultData; // 操作后的图像数据
this.savedState = null;
}
/**
* 执行液化操作
* @returns {Promise} 执行结果
*/
async execute() {
if (!this.canvas || !this.targetObject) {
throw new Error("液化命令缺少必要的画布或目标对象");
}
if (!this.resultData) {
// 如果没有预先计算的结果数据,现场执行变形
this.resultData = await this.liquifyManager.applyLiquify(
this.targetObject,
this.mode,
this.params
);
}
// 保存执行前的状态
this.savedState = await this._saveObjectState();
// 更新画布上的对象
await this._updateObjectWithResult();
// 刷新Canvas
this.canvas.renderAll();
return this.resultData;
}
/**
* 撤销液化操作
* @returns {Promise} 撤销结果
*/
async undo() {
if (!this.canvas || !this.targetObject || !this.savedState) {
throw new Error("无法撤销:缺少必要的状态信息");
}
// 恢复对象到原始状态
await this._restoreObjectState();
// 刷新Canvas
this.canvas.renderAll();
return true;
}
/**
* 保存对象状态
* @private
*/
async _saveObjectState() {
if (!this.targetObject) return null;
// 对于图像对象我们需要保存src和元数据
const state = {
src: this.targetObject.getSrc ? this.targetObject.getSrc() : null,
element: this.targetObject._element
? this.targetObject._element.cloneNode(true)
: null,
filters: this.targetObject.filters ? [...this.targetObject.filters] : [],
originalData: this.originalData,
targetLayerId: this.targetLayerId,
};
return state;
}
/**
* 恢复对象状态
* @private
*/
async _restoreObjectState() {
if (!this.targetObject || !this.savedState) return false;
// 获取当前图层对象
const layer = this.layerManager.getLayerById(this.savedState.targetLayerId);
if (!layer) return false;
// 恢复原始图像
if (this.savedState.element && this.targetObject.setElement) {
this.targetObject.setElement(this.savedState.element);
// 恢复滤镜
if (this.savedState.filters) {
this.targetObject.filters = [...this.savedState.filters];
this.targetObject.applyFilters();
}
}
return true;
}
/**
* 使用变形结果更新对象
* @private
*/
async _updateObjectWithResult() {
if (!this.targetObject || !this.resultData) return false;
// 创建临时canvas来渲染结果数据
const tempCanvas = document.createElement("canvas");
tempCanvas.width = this.resultData.width;
tempCanvas.height = this.resultData.height;
const tempCtx = tempCanvas.getContext("2d");
tempCtx.putImageData(this.resultData, 0, 0);
console.log("临时Canvas创建成功 _updateObjectWithResult", this.resultData);
// 更新Fabric图像
await new Promise((resolve) => {
fabric.Image.fromURL(tempCanvas.toDataURL(), (img) => {
// 保留原对象的属性
img.set({
left: this.targetObject.left,
top: this.targetObject.top,
scaleX: this.targetObject.scaleX,
scaleY: this.targetObject.scaleY,
angle: this.targetObject.angle,
flipX: this.targetObject.flipX,
flipY: this.targetObject.flipY,
opacity: this.targetObject.opacity,
});
// 替换Canvas上的对象
const index = this.canvas.getObjects().indexOf(this.targetObject);
if (index !== -1) {
this.canvas.remove(this.targetObject);
this.canvas.insertAt(img, index);
this.targetObject = img;
}
// 确保图层引用更新
const layer = this.layerManager.getLayerById(this.targetLayerId);
if (layer) {
if (
layer.type === "background" &&
layer.fabricObject === this.targetObject
) {
layer.fabricObject = img;
} else if (layer.fabricObjects) {
const objIndex = layer.fabricObjects.indexOf(this.targetObject);
if (objIndex !== -1) {
layer.fabricObjects[objIndex] = img;
}
}
}
resolve();
});
});
return true;
}
}
/**
* 液化工具初始化命令
* 用于初始化液化工具的状态不执行实际操作
*/
export class InitLiquifyToolCommand extends Command {
constructor(options) {
super({
name: "初始化液化工具",
description: "准备液化工具工作环境",
undoable: false, // 这个命令不需要撤销
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.liquifyManager = options.liquifyManager;
this.toolManager = options.toolManager;
}
/**
* 执行初始化
*/
execute() {
if (this.liquifyManager) {
this.liquifyManager.initialize({
canvas: this.canvas,
layerManager: this.layerManager,
});
}
// 通知各管理器进入液化模式
this.toolManager?.notifyObservers("LIQUIFY");
return true;
}
undo() {
// 不需要撤销初始化
return true;
}
}
/**
* 液化操作命令 - 针对单次变形操作
* 用于实现可撤销的单次液化变形
*/
export class LiquifyDeformCommand extends LiquifyCommand {
/**
* 创建液化变形命令
* @param {Object} options 配置选项
* @param {Number} options.x 变形中心X坐标
* @param {Number} options.y 变形中心Y坐标
* @param {ImageData} options.beforeData 变形前的图像数据
* @param {ImageData} options.afterData 变形后的图像数据
*/
constructor(options) {
super({
...options,
name: `液化变形: ${options.mode || "未知模式"}`,
description: `在(${options.x}, ${options.y})应用${
options.mode || "未知模式"
}变形`,
});
this.x = options.x;
this.y = options.y;
this.beforeData = options.beforeData;
this.afterData = options.afterData;
}
async execute() {
if (!this.afterData) {
// 如果没有预计算的结果,实时计算
await this.liquifyManager.prepareForLiquify(this.targetObject);
this.afterData = await this.liquifyManager.applyLiquify(
this.targetObject,
this.mode,
this.params,
this.x,
this.y
);
}
// 保存当前状态
this.savedState = await this._saveObjectState();
// 应用变形结果
await this._updateObjectWithImageData(this.afterData);
this.canvas.renderAll();
return this.afterData;
}
async undo() {
if (!this.beforeData) {
throw new Error("无法撤销:缺少变形前的数据");
}
// 恢复到变形前的状态
await this._updateObjectWithImageData(this.beforeData);
this.canvas.renderAll();
return true;
}
/**
* 使用图像数据更新对象
* @param {ImageData} imageData 图像数据
* @private
*/
async _updateObjectWithImageData(imageData) {
return new Promise((resolve) => {
// 创建临时canvas
const tempCanvas = document.createElement("canvas");
tempCanvas.width = imageData.width;
tempCanvas.height = imageData.height;
const tempCtx = tempCanvas.getContext("2d");
tempCtx.putImageData(imageData, 0, 0);
// 从canvas创建新的fabric图像
fabric.Image.fromURL(tempCanvas.toDataURL(), (img) => {
// 保留原对象的变换属性
img.set({
left: this.targetObject.left,
top: this.targetObject.top,
scaleX: this.targetObject.scaleX,
scaleY: this.targetObject.scaleY,
angle: this.targetObject.angle,
flipX: this.targetObject.flipX,
flipY: this.targetObject.flipY,
opacity: this.targetObject.opacity,
});
// 替换canvas上的对象
const index = this.canvas.getObjects().indexOf(this.targetObject);
if (index !== -1) {
this.canvas.remove(this.targetObject);
this.canvas.insertAt(img, index);
this.targetObject = img;
// 更新图层引用
const layer = this.layerManager.getLayerById(this.targetLayerId);
if (layer) {
if (
layer.type === "background" &&
(layer.fabricObject === this.savedState?.originalObject ||
layer.fabricObject === this.targetObject)
) {
layer.fabricObject = img;
} else if (layer.fabricObjects) {
const objIndex = layer.fabricObjects.findIndex(
(obj) =>
obj === this.savedState?.originalObject ||
obj === this.targetObject
);
if (objIndex !== -1) {
layer.fabricObjects[objIndex] = img;
}
}
}
}
resolve();
});
});
}
}
/**
* 复合液化命令 - 用于组合多个液化操作
* 支持一次撤销多个相关的液化变形
*/
export class CompositeLiquifyCommand extends Command {
/**
* 创建复合液化命令
* @param {Object} options 配置选项
* @param {Array} options.commands 子命令列表
* @param {String} options.name 命令名称
*/
constructor(options) {
super({
name: options.name || "液化操作组合",
description:
options.description || `包含${options.commands?.length || 0}个液化操作`,
});
this.commands = options.commands || [];
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.liquifyManager = options.liquifyManager;
}
/**
* 添加子命令
* @param {Command} command 要添加的命令
*/
addCommand(command) {
this.commands.push(command);
}
async execute() {
const results = [];
for (const command of this.commands) {
try {
const result = await command.execute();
results.push(result);
} catch (error) {
// 如果有命令失败,尝试回滚已执行的命令
console.error("复合液化命令执行失败:", error);
await this.undo();
throw error;
}
}
return results;
}
async undo() {
// 逆序撤销所有子命令
const errors = [];
for (let i = this.commands.length - 1; i >= 0; i--) {
try {
await this.commands[i].undo();
} catch (error) {
console.error(`撤销子命令${i}失败:`, error);
errors.push(error);
}
}
if (errors.length > 0) {
throw new Error(`复合命令撤销部分失败: ${errors.length}个错误`);
}
return true;
}
/**
* 检查命令是否可以执行
* @returns {Boolean} 是否可执行
*/
canExecute() {
return (
this.commands.length > 0 &&
this.commands.every((cmd) => (cmd.canExecute ? cmd.canExecute() : true))
);
}
}
/**
* 液化重置命令 - 将图像恢复到原始状态
*/
export class LiquifyResetCommand extends LiquifyCommand {
constructor(options) {
super({
...options,
name: "重置液化",
description: "将图像恢复到液化前的原始状态",
});
}
async execute() {
if (!this.liquifyManager || !this.targetObject) {
throw new Error("无法重置:缺少必要的管理器或目标对象");
}
// 保存当前状态
this.savedState = await this._saveObjectState();
// 重置液化管理器
const resetData = this.liquifyManager.reset();
if (!resetData) {
throw new Error("重置失败:没有原始数据");
}
// 应用重置结果
await this._updateObjectWithResult();
this.canvas.renderAll();
return resetData;
}
}
2025-06-18 11:05:23 +08:00
/**
* 液化状态命令 - 最优化版本
* 使用引用管理器避免对象引用丢失支持高性能的状态管理
*/
export class LiquifyStateCommand extends Command {
/**
* 创建液化状态命令
* @param {Object} options 配置选项
* @param {Object} options.canvas Fabric.js画布实例
* @param {Object} options.layerManager 图层管理器实例
* @param {Object} options.targetObject 目标对象保持引用
* @param {String} options.targetLayerId 目标图层ID
* @param {ImageData} options.initialImageData 初始图像数据
* @param {ImageData} options.finalImageData 最终图像数据
*/
constructor(options) {
super({
name: options.name || "液化操作",
description: options.description || "液化变形操作的状态记录",
});
this.canvas = options.canvas;
this.layerManager = options.layerManager;
this.targetObject = options.targetObject;
this.targetLayerId = options.targetLayerId;
this.targetObjectId = options.targetObjectId;
// 获取引用管理器实例
this.refManager = getLiquifyReferenceManager();
// 注册对象到引用管理器
this.objectRefId = this.refManager.registerObject(
this.targetObject,
this.targetObjectId || `liquify_${Date.now()}`
);
// 保存状态快照ID
this.initialSnapshotId = null;
this.finalSnapshotId = null;
// 设置图像数据
this.initialImageData = options.initialImageData;
this.finalImageData = options.finalImageData;
this.currentState = "initial";
// 创建初始快照
if (this.initialImageData) {
this._createInitialSnapshot();
}
}
/**
* 执行命令 - 应用最终状态
*/
async execute() {
if (!this.finalImageData) {
throw new Error("缺少最终状态数据");
}
// 确保有最终状态快照
if (!this.finalSnapshotId) {
await this._createFinalSnapshot();
}
// 通过引用管理器更新图像数据
await this.refManager.updateObjectImageData(
this.objectRefId,
this.finalImageData
);
this.currentState = "final";
this.canvas.renderAll();
console.log("✅ 液化命令执行完成,应用最终状态");
return true;
}
/**
* 撤销命令 - 恢复初始状态
*/
async undo() {
if (!this.initialImageData || !this.initialSnapshotId) {
throw new Error("缺少初始状态数据或快照");
}
// 通过引用管理器恢复到初始快照
await this.refManager.restoreFromSnapshot(
this.objectRefId,
this.initialSnapshotId
);
this.currentState = "initial";
this.canvas.renderAll();
console.log("🔄 液化命令撤销完成,恢复初始状态");
return true;
}
/**
* 重做命令 - 重新应用最终状态
*/
async redo() {
return this.execute();
}
/**
* 设置最终状态的图像数据
* @param {ImageData} finalImageData 最终状态的图像数据
*/
setFinalImageData(finalImageData) {
this.finalImageData = finalImageData;
this.finalSnapshotId = null; // 重置快照ID下次执行时重新创建
}
/**
* 获取当前状态标记
* @returns {String} 'initial' 'final'
*/
getCurrentState() {
return this.currentState;
}
/**
* 检查命令是否有效
* @returns {Boolean} 是否有效
*/
isValid() {
const targetObject = this.refManager.getObjectRef(this.objectRefId);
return !!(targetObject && this.initialImageData && this.finalImageData);
}
/**
* 获取目标对象的当前引用
* @returns {Object|null} Fabric对象
*/
getTargetObject() {
return this.refManager.getObjectRef(this.objectRefId);
}
/**
* 清理资源
*/
dispose() {
if (this.objectRefId) {
// 清理快照
if (this.initialSnapshotId) {
this.refManager.stateSnapshots.delete(this.initialSnapshotId);
}
if (this.finalSnapshotId) {
this.refManager.stateSnapshots.delete(this.finalSnapshotId);
}
// 注意:不要清理对象引用,因为可能有其他命令在使用
console.log(`🗑️ 液化状态命令资源已清理: ${this.objectRefId}`);
}
}
/**
* 获取内存使用统计
* @returns {Object} 统计信息
*/
getMemoryStats() {
const refManagerStats = this.refManager.getMemoryStats();
const commandMemory = this._calculateCommandMemory();
return {
...refManagerStats,
commandMemory,
totalCommandMemory: commandMemory,
};
}
// 私有方法
/**
* 创建初始状态快照
* @private
*/
async _createInitialSnapshot() {
if (!this.initialImageData) return;
this.initialSnapshotId = `${this.objectRefId}_initial_${Date.now()}`;
// 手动创建初始快照,包含图像数据
const fabricObject = this.refManager.getObjectRef(this.objectRefId);
if (fabricObject) {
const snapshot = {
timestamp: Date.now(),
properties: this.refManager._captureObjectState(fabricObject),
imageData: this.initialImageData,
eventListeners:
this.refManager.eventListeners.get(this.objectRefId) || {},
};
this.refManager.stateSnapshots.set(this.initialSnapshotId, snapshot);
console.log(`📸 初始状态快照已创建: ${this.initialSnapshotId}`);
}
}
/**
* 创建最终状态快照
* @private
*/
async _createFinalSnapshot() {
if (!this.finalImageData) return;
this.finalSnapshotId = `${this.objectRefId}_final_${Date.now()}`;
// 手动创建最终快照,包含图像数据
const fabricObject = this.refManager.getObjectRef(this.objectRefId);
if (fabricObject) {
const snapshot = {
timestamp: Date.now(),
properties: this.refManager._captureObjectState(fabricObject),
imageData: this.finalImageData,
eventListeners:
this.refManager.eventListeners.get(this.objectRefId) || {},
};
this.refManager.stateSnapshots.set(this.finalSnapshotId, snapshot);
console.log(`📸 最终状态快照已创建: ${this.finalSnapshotId}`);
}
}
/**
* 计算命令本身的内存使用量
* @returns {Number} 内存使用量字节
* @private
*/
_calculateCommandMemory() {
let bytes = 0;
// 计算ImageData内存使用
if (this.initialImageData) {
bytes += this.initialImageData.width * this.initialImageData.height * 4;
}
if (this.finalImageData) {
bytes += this.finalImageData.width * this.finalImageData.height * 4;
}
return bytes;
}
}
/**
* 批量液化状态命令 - 用于处理多个对象的液化操作
*/
export class BatchLiquifyStateCommand extends Command {
/**
* 创建批量液化状态命令
* @param {Object} options 配置选项
* @param {Array} options.commands 液化状态命令列表
* @param {Object} options.canvas Fabric.js画布实例
*/
constructor(options) {
super({
name: options.name || "批量液化操作",
description:
options.description || `批量液化${options.commands?.length || 0}个对象`,
});
this.commands = options.commands || [];
this.canvas = options.canvas;
this.refManager = getLiquifyReferenceManager();
}
/**
* 添加液化状态命令
* @param {LiquifyStateCommand} command 液化状态命令
*/
addCommand(command) {
this.commands.push(command);
}
/**
* 执行批量操作
*/
async execute() {
const results = [];
const updates = [];
// 准备批量更新数据
for (const command of this.commands) {
if (command.finalImageData && command.objectRefId) {
updates.push({
refId: command.objectRefId,
imageData: command.finalImageData,
});
}
}
// 执行批量更新
if (updates.length > 0) {
const updateResults = await this.refManager.batchUpdate(updates);
results.push(...updateResults);
}
// 更新命令状态
this.commands.forEach((command) => {
command.currentState = "final";
});
this.canvas.renderAll();
return results;
}
/**
* 撤销批量操作
*/
async undo() {
// 逆序撤销
for (let i = this.commands.length - 1; i >= 0; i--) {
await this.commands[i].undo();
}
this.canvas.renderAll();
return true;
}
/**
* 重做批量操作
*/
async redo() {
return this.execute();
}
/**
* 检查批量命令是否有效
* @returns {Boolean} 是否有效
*/
isValid() {
return (
this.commands.length > 0 && this.commands.every((cmd) => cmd.isValid())
);
}
/**
* 清理所有子命令的资源
*/
dispose() {
this.commands.forEach((command) => {
if (command.dispose) {
command.dispose();
}
});
}
}
/**
* 序列化fabric对象为JSON
* @param {Object} fabricObject fabric对象
* @returns {Object} 序列化后的对象状态
*/
export function serializeFabricObject(fabricObject) {
if (!fabricObject) {
throw new Error("无法序列化:对象为空");
}
try {
// 使用fabric.js的toObject方法序列化
const serializedObject = fabricObject.toObject([
"id",
"objectId",
"uid",
"layerId",
"name",
"type",
]);
// 记录额外的元数据
const metadata = {
timestamp: Date.now(),
objectType: fabricObject.type,
objectId: fabricObject.id || fabricObject.objectId || fabricObject.uid,
layerId: fabricObject.layerId,
bounds: fabricObject.getBoundingRect(),
};
return {
serializedObject,
metadata,
version: "1.0",
};
} catch (error) {
console.error("序列化fabric对象失败:", error);
throw error;
}
}
/**
* 辅助函数创建液化状态命令
* @param {Object} options 配置选项
* @returns {LiquifyStateCommand} 状态命令实例
*/
export function createLiquifyStateCommand(options) {
return new LiquifyStateCommand(options);
}
2025-06-09 10:25:54 +08:00
/**
* 辅助函数创建液化重置命令
* @param {Object} options 配置选项
* @returns {LiquifyResetCommand} 重置命令实例
*/
export function createLiquifyResetCommand(options) {
return new LiquifyResetCommand(options);
}
/**
* 辅助函数创建液化变形命令
* @param {Object} options 配置选项
* @returns {LiquifyDeformCommand} 变形命令实例
*/
export function createLiquifyDeformCommand(options) {
return new LiquifyDeformCommand(options);
}
/**
* 辅助函数创建复合液化命令
* @param {Object} options 配置选项
* @returns {CompositeLiquifyCommand} 复合命令实例
*/
export function createCompositeLiquifyCommand(options) {
return new CompositeLiquifyCommand(options);
}