1229 lines
36 KiB
JavaScript
1229 lines
36 KiB
JavaScript
import { Command, FunctionCommand } from "./Command";
|
||
import { getLiquifyReferenceManager } from "../managers/LiquifyReferenceManager";
|
||
import { optimizeCanvasRendering } from "../utils/helper";
|
||
import { fabric } from "fabric-with-all";
|
||
|
||
/**
|
||
* 液化命令基类
|
||
* 所有液化相关命令的基类
|
||
*/
|
||
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();
|
||
// 注意:_updateObjectWithResult 内部已使用 optimizeCanvasRendering,会自动渲染
|
||
|
||
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;
|
||
}
|
||
|
||
/**
|
||
* 使用变形结果更新对象
|
||
* 优化:直接使用 setElement 更新现有对象,避免创建新对象和替换操作
|
||
* @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);
|
||
|
||
// 使用优化渲染工具,避免图层闪烁
|
||
await optimizeCanvasRendering(this.canvas, async () => {
|
||
// 预加载图像元素,确保完全加载后再更新
|
||
await this._updateObjectElementDirectly(tempCanvas.toDataURL());
|
||
});
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 直接更新对象的图像元素,避免对象替换造成的图层闪烁
|
||
* @param {String} imageDataURL 图像数据的 DataURL
|
||
* @private
|
||
*/
|
||
async _updateObjectElementDirectly(imageDataURL) {
|
||
return new Promise((resolve, reject) => {
|
||
// 创建 HTMLImageElement 并预加载
|
||
const imgElement = new Image();
|
||
|
||
imgElement.onload = () => {
|
||
try {
|
||
// 保存当前对象状态(非图像属性)
|
||
const currentState = {
|
||
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,
|
||
};
|
||
|
||
// 直接更新现有对象的图像元素,保持对象引用不变
|
||
if (this.targetObject.setElement) {
|
||
this.targetObject.setElement(imgElement);
|
||
} else if (this.targetObject._element) {
|
||
this.targetObject._element = imgElement;
|
||
this.targetObject._originalElement = imgElement;
|
||
}
|
||
|
||
// 恢复对象属性(setElement 可能会重置一些属性)
|
||
this.targetObject.set(currentState);
|
||
|
||
// 标记对象需要重新渲染
|
||
this.targetObject.dirty = true;
|
||
this.targetObject.setCoords();
|
||
|
||
resolve();
|
||
} catch (error) {
|
||
console.error("更新对象元素失败:", error);
|
||
reject(error);
|
||
}
|
||
};
|
||
|
||
imgElement.onerror = () => {
|
||
reject(new Error("图像加载失败"));
|
||
};
|
||
|
||
// 开始加载图像
|
||
imgElement.src = imageDataURL;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 安全地替换对象,避免图层闪烁(保留作为备用方法)
|
||
* @param {Object} newImg 新的fabric图像对象
|
||
* @private
|
||
*/
|
||
_replaceObjectSafely(newImg) {
|
||
// 保存旧对象引用,用于更新图层引用
|
||
const oldObject = this.targetObject;
|
||
|
||
// 替换Canvas上的对象
|
||
const index = this.canvas.getObjects().indexOf(oldObject);
|
||
if (index !== -1) {
|
||
// 在禁用自动渲染的情况下,先插入新对象,再移除旧对象
|
||
// 这样可以避免中间出现空白(因为renderOnAddRemove已被禁用)
|
||
this.canvas.insertAt(newImg, index);
|
||
this.canvas.remove(oldObject);
|
||
this.targetObject = newImg;
|
||
}
|
||
|
||
// 确保图层引用更新
|
||
const layer = this.layerManager.getLayerById(this.targetLayerId);
|
||
if (layer) {
|
||
if (layer.type === "background" && layer.fabricObject === oldObject) {
|
||
layer.fabricObject = newImg;
|
||
} else if (layer.fabricObjects) {
|
||
const objIndex = layer.fabricObjects.indexOf(oldObject);
|
||
if (objIndex !== -1) {
|
||
layer.fabricObjects[objIndex] = newImg;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 液化工具初始化命令
|
||
* 用于初始化液化工具的状态,不执行实际操作
|
||
*/
|
||
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);
|
||
// 注意:_updateObjectWithImageData 内部已使用 optimizeCanvasRendering,会自动渲染
|
||
|
||
return this.afterData;
|
||
}
|
||
|
||
async undo() {
|
||
if (!this.beforeData) {
|
||
throw new Error("无法撤销:缺少变形前的数据");
|
||
}
|
||
|
||
// 恢复到变形前的状态
|
||
await this._updateObjectWithImageData(this.beforeData);
|
||
// 注意:_updateObjectWithImageData 内部已使用 optimizeCanvasRendering,会自动渲染
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 使用图像数据更新对象
|
||
* 优化:直接使用 setElement 更新现有对象,避免创建新对象和替换操作
|
||
* @param {ImageData} imageData 图像数据
|
||
* @private
|
||
*/
|
||
async _updateObjectWithImageData(imageData) {
|
||
// 创建临时canvas
|
||
const tempCanvas = document.createElement("canvas");
|
||
tempCanvas.width = imageData.width;
|
||
tempCanvas.height = imageData.height;
|
||
const tempCtx = tempCanvas.getContext("2d");
|
||
tempCtx.putImageData(imageData, 0, 0);
|
||
|
||
// 使用优化渲染工具,避免图层闪烁
|
||
await optimizeCanvasRendering(this.canvas, async () => {
|
||
// 直接更新对象元素,避免对象替换
|
||
await this._updateObjectElementDirectly(tempCanvas.toDataURL());
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 安全地替换变形对象,避免图层闪烁(保留作为备用方法)
|
||
* @param {Object} newImg 新的fabric图像对象
|
||
* @private
|
||
*/
|
||
_replaceDeformObjectSafely(newImg) {
|
||
// 保存旧对象引用,用于更新图层引用
|
||
const oldObject = this.targetObject;
|
||
|
||
// 替换Canvas上的对象
|
||
const index = this.canvas.getObjects().indexOf(oldObject);
|
||
if (index !== -1) {
|
||
// 在禁用自动渲染的情况下,先插入新对象,再移除旧对象
|
||
// 这样可以避免中间出现空白
|
||
this.canvas.insertAt(newImg, index);
|
||
this.canvas.remove(oldObject);
|
||
this.targetObject = newImg;
|
||
}
|
||
|
||
// 更新图层引用
|
||
const layer = this.layerManager.getLayerById(this.targetLayerId);
|
||
if (layer) {
|
||
if (
|
||
layer.type === "background" &&
|
||
(layer.fabricObject === this.savedState?.originalObject ||
|
||
layer.fabricObject === oldObject)
|
||
) {
|
||
layer.fabricObject = newImg;
|
||
} else if (layer.fabricObjects) {
|
||
const objIndex = layer.fabricObjects.findIndex(
|
||
(obj) => obj === this.savedState?.originalObject || obj === oldObject
|
||
);
|
||
if (objIndex !== -1) {
|
||
layer.fabricObjects[objIndex] = newImg;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 复合液化命令 - 用于组合多个液化操作
|
||
* 支持一次撤销多个相关的液化变形
|
||
*/
|
||
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;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 液化状态命令 - 最优化版本
|
||
* 使用引用管理器避免对象引用丢失,支持高性能的状态管理
|
||
*/
|
||
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 最终图像数据
|
||
* @param {Object} options.liquifyManager 液化管理器实例
|
||
*/
|
||
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.liquifyManager = options.liquifyManager; // 添加液化管理器引用
|
||
|
||
// 获取引用管理器实例
|
||
this.refManager = getLiquifyReferenceManager();
|
||
|
||
// 实时更新实例
|
||
this.realtimeUpdater = options.realtimeUpdater || null;
|
||
|
||
// 注册对象到引用管理器
|
||
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.initialLiquifyState = null;
|
||
this.finalLiquifyState = null;
|
||
|
||
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);
|
||
|
||
// 恢复液化管理器到最终状态
|
||
if (this.liquifyManager && this.finalLiquifyState) {
|
||
this._restoreLiquifyManagerState(this.finalLiquifyState);
|
||
} else if (this.liquifyManager) {
|
||
// 如果没有保存的最终状态,重新准备液化环境
|
||
const currentTarget = this.refManager.getObjectRef(this.objectRefId);
|
||
if (currentTarget) {
|
||
await this.liquifyManager.prepareForLiquify(currentTarget);
|
||
// 保存当前的液化管理器状态作为最终状态
|
||
this.finalLiquifyState = this._captureLiquifyManagerState();
|
||
}
|
||
}
|
||
|
||
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);
|
||
|
||
// 恢复液化管理器到初始状态 - 关键修复
|
||
if (this.liquifyManager) {
|
||
// if (this.initialLiquifyState) {
|
||
// this._restoreLiquifyManagerState(this.initialLiquifyState);
|
||
// } else {
|
||
// 如果没有初始状态,重置液化管理器并重新初始化
|
||
this.liquifyManager.reset();
|
||
const currentTarget = this.refManager.getObjectRef(this.objectRefId);
|
||
if (currentTarget) {
|
||
// 重新准备液化环境,使用初始图像数据
|
||
await this.liquifyManager.prepareForLiquify(currentTarget);
|
||
|
||
// 强制设置原始图像数据为初始状态
|
||
if (this.liquifyManager.enhancedManager) {
|
||
this.liquifyManager.enhancedManager.originalImageData = this.initialImageData;
|
||
this.liquifyManager.enhancedManager.currentImageData = this._cloneImageData(
|
||
this.initialImageData
|
||
);
|
||
|
||
// 重置激活渲染器的数据
|
||
if (this.liquifyManager.enhancedManager.activeRenderer) {
|
||
const renderer = this.liquifyManager.enhancedManager.activeRenderer;
|
||
renderer.originalImageData = this.initialImageData;
|
||
renderer.currentImageData = this._cloneImageData(this.initialImageData);
|
||
|
||
// 重置CPU渲染器的网格和变形历史
|
||
if (renderer.reset) {
|
||
renderer.reset();
|
||
}
|
||
// }
|
||
}
|
||
}
|
||
}
|
||
|
||
// 确保实时更新器使用正确的目标对象和初始数据
|
||
if (this.realtimeUpdater) {
|
||
const currentTarget = this.refManager.getObjectRef(this.objectRefId);
|
||
this.realtimeUpdater.targetObject = currentTarget;
|
||
// 重置实时更新器的内部状态
|
||
if (this.realtimeUpdater.reset) {
|
||
this.realtimeUpdater.reset();
|
||
}
|
||
}
|
||
}
|
||
|
||
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,下次执行时重新创建
|
||
|
||
// 捕获当前液化管理器状态作为最终状态
|
||
if (this.liquifyManager) {
|
||
this.finalLiquifyState = this._captureLiquifyManagerState();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取当前状态标记
|
||
* @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);
|
||
|
||
// 捕获初始液化管理器状态
|
||
if (this.liquifyManager) {
|
||
this.initialLiquifyState = this._captureLiquifyManagerState();
|
||
}
|
||
|
||
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);
|
||
|
||
// 捕获最终液化管理器状态
|
||
if (this.liquifyManager) {
|
||
this.finalLiquifyState = this._captureLiquifyManagerState();
|
||
}
|
||
|
||
console.log(`📸 最终状态快照已创建: ${this.finalSnapshotId}`);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 捕获液化管理器的当前状态
|
||
* @returns {Object} 液化管理器状态
|
||
* @private
|
||
*/
|
||
_captureLiquifyManagerState() {
|
||
if (!this.liquifyManager) return null;
|
||
|
||
try {
|
||
const enhancedManager = this.liquifyManager.enhancedManager;
|
||
|
||
const state = {
|
||
// 捕获增强管理器状态
|
||
enhancedManagerState: null,
|
||
// 捕获当前渲染器状态
|
||
activeRendererState: null,
|
||
// 捕获目标对象引用
|
||
targetObjectRef: this.liquifyManager.targetObject ?? enhancedManager.targetObject,
|
||
// 捕获初始化状态
|
||
initialized: this.liquifyManager.initialized || false,
|
||
};
|
||
|
||
// 如果有增强管理器,捕获其状态
|
||
if (this.liquifyManager.enhancedManager) {
|
||
state.enhancedManagerState = {
|
||
initialized: enhancedManager.initialized,
|
||
renderMode: enhancedManager.renderMode,
|
||
targetObject: enhancedManager.targetObject,
|
||
originalImageData: enhancedManager.originalImageData,
|
||
currentImageData: enhancedManager.currentImageData,
|
||
params: { ...enhancedManager.params },
|
||
currentMode: enhancedManager.currentMode,
|
||
};
|
||
|
||
// 如果有激活的渲染器,捕获其状态
|
||
if (enhancedManager.activeRenderer) {
|
||
const renderer = enhancedManager.activeRenderer;
|
||
state.activeRendererState = {
|
||
initialized: renderer.initialized,
|
||
originalImageData: renderer.originalImageData,
|
||
currentImageData: renderer.currentImageData,
|
||
params: { ...renderer.params },
|
||
currentMode: renderer.currentMode,
|
||
// 对于CPU渲染器,还需要保存网格状态
|
||
meshState: renderer.mesh ? this._captureMeshState(renderer.mesh) : null,
|
||
// 保存变形历史
|
||
deformHistory: renderer.deformHistory ? [...renderer.deformHistory] : [],
|
||
};
|
||
}
|
||
}
|
||
|
||
console.log(`💾 液化管理器状态已捕获:`, state);
|
||
return state;
|
||
} catch (error) {
|
||
console.error("捕获液化管理器状态失败:", error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 恢复液化管理器状态
|
||
* @param {Object} state 要恢复的状态
|
||
* @private
|
||
*/
|
||
_restoreLiquifyManagerState(state) {
|
||
if (!this.liquifyManager || !state) return;
|
||
|
||
try {
|
||
// 恢复基本状态
|
||
this.liquifyManager.initialized = state.initialized;
|
||
if (state.targetObjectRef) {
|
||
this.liquifyManager.targetObject = state.targetObjectRef;
|
||
}
|
||
|
||
// 确保实时更新器使用正确的目标对象
|
||
if (this.realtimeUpdater) {
|
||
this.realtimeUpdater.targetObject = this.liquifyManager.targetObject;
|
||
// 如果有setTargetObject方法,调用它
|
||
if (typeof this.realtimeUpdater.setTargetObject === "function") {
|
||
this.realtimeUpdater.setTargetObject(this.liquifyManager.targetObject);
|
||
}
|
||
}
|
||
|
||
// 恢复增强管理器状态
|
||
if (state.enhancedManagerState && this.liquifyManager.enhancedManager) {
|
||
const enhancedManager = this.liquifyManager.enhancedManager;
|
||
const enhancedState = state.enhancedManagerState;
|
||
|
||
enhancedManager.initialized = enhancedState.initialized;
|
||
enhancedManager.renderMode = enhancedState.renderMode;
|
||
enhancedManager.targetObject = enhancedState.targetObject;
|
||
|
||
// 关键修复:确保图像数据被正确恢复
|
||
enhancedManager.originalImageData = enhancedState.originalImageData;
|
||
enhancedManager.currentImageData = enhancedState.currentImageData;
|
||
enhancedManager.params = { ...enhancedState.params };
|
||
enhancedManager.currentMode = enhancedState.currentMode;
|
||
|
||
// 恢复激活渲染器状态
|
||
if (state.activeRendererState && enhancedManager.activeRenderer) {
|
||
const renderer = enhancedManager.activeRenderer;
|
||
const rendererState = state.activeRendererState;
|
||
|
||
renderer.initialized = rendererState.initialized;
|
||
|
||
// 关键修复:强制重置渲染器的图像数据
|
||
renderer.originalImageData = rendererState.originalImageData;
|
||
renderer.currentImageData = rendererState.currentImageData;
|
||
renderer.params = { ...rendererState.params };
|
||
renderer.currentMode = rendererState.currentMode;
|
||
|
||
// 恢复网格状态(如果是CPU渲染器)
|
||
if (rendererState.meshState && renderer.mesh) {
|
||
this._restoreMeshState(renderer.mesh, rendererState.meshState);
|
||
} else if (renderer.mesh && renderer._initMesh) {
|
||
// 如果没有保存的网格状态,重新初始化网格
|
||
renderer._initMesh(
|
||
rendererState.originalImageData?.width || renderer.originalImageData?.width,
|
||
rendererState.originalImageData?.height || renderer.originalImageData?.height
|
||
);
|
||
}
|
||
|
||
// 重置变形历史
|
||
if (rendererState.deformHistory) {
|
||
renderer.deformHistory = [...rendererState.deformHistory];
|
||
} else {
|
||
// 如果没有保存的历史,清空变形历史
|
||
renderer.deformHistory = [];
|
||
}
|
||
|
||
// 重置持续按压相关状态(如果存在)
|
||
if (renderer.isHolding !== undefined) {
|
||
renderer.isHolding = false;
|
||
renderer.pressStartTime = 0;
|
||
renderer.pressDuration = 0;
|
||
renderer.accumulatedRotation = 0;
|
||
renderer.accumulatedScale = 0;
|
||
renderer.lastApplyTime = 0;
|
||
}
|
||
|
||
// 重置拖拽状态(如果存在)
|
||
if (renderer.isDragging !== undefined) {
|
||
renderer.isDragging = false;
|
||
renderer.dragDistance = 0;
|
||
renderer.dragAngle = 0;
|
||
renderer.isFirstApply = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
console.log(`🔄 液化管理器状态已恢复(包含完整的数据重置)`);
|
||
} catch (error) {
|
||
console.error("恢复液化管理器状态失败:", error);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 捕获网格状态
|
||
* @param {Array} mesh 网格数组
|
||
* @returns {Array} 网格状态副本
|
||
* @private
|
||
*/
|
||
_captureMeshState(mesh) {
|
||
if (!mesh || !Array.isArray(mesh)) return null;
|
||
|
||
return mesh.map((row) =>
|
||
row.map((point) => ({
|
||
x: point.x,
|
||
y: point.y,
|
||
originalX: point.originalX,
|
||
originalY: point.originalY,
|
||
}))
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 恢复网格状态
|
||
* @param {Array} mesh 目标网格
|
||
* @param {Array} meshState 要恢复的网格状态
|
||
* @private
|
||
*/
|
||
_restoreMeshState(mesh, meshState) {
|
||
if (!mesh || !meshState || !Array.isArray(mesh) || !Array.isArray(meshState)) return;
|
||
|
||
for (let i = 0; i < Math.min(mesh.length, meshState.length); i++) {
|
||
for (let j = 0; j < Math.min(mesh[i].length, meshState[i].length); j++) {
|
||
const point = mesh[i][j];
|
||
const statePoint = meshState[i][j];
|
||
|
||
point.x = statePoint.x;
|
||
point.y = statePoint.y;
|
||
point.originalX = statePoint.originalX;
|
||
point.originalY = statePoint.originalY;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 克隆图像数据
|
||
* @param {ImageData} imageData 原始图像数据
|
||
* @returns {ImageData} 克隆的图像数据
|
||
* @private
|
||
*/
|
||
_cloneImageData(imageData) {
|
||
if (!imageData) return null;
|
||
|
||
try {
|
||
// 使用新的浏览器API直接复制
|
||
return new ImageData(
|
||
new Uint8ClampedArray(imageData.data),
|
||
imageData.width,
|
||
imageData.height
|
||
);
|
||
} catch (e) {
|
||
console.warn("使用备选方法克隆ImageData");
|
||
// 备选方法
|
||
const canvas = document.createElement("canvas");
|
||
const ctx = canvas.getContext("2d");
|
||
|
||
canvas.width = imageData.width;
|
||
canvas.height = imageData.height;
|
||
|
||
ctx.putImageData(imageData, 0, 0);
|
||
return ctx.getImageData(0, 0, imageData.width, imageData.height);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 批量液化状态命令 - 用于处理多个对象的液化操作
|
||
*/
|
||
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);
|
||
}
|
||
|
||
/**
|
||
* 辅助函数:创建液化重置命令
|
||
* @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);
|
||
}
|