合并画布代码
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Command, FunctionCommand } from "./Command";
|
||||
import { getLiquifyReferenceManager } from "../managers/LiquifyReferenceManager";
|
||||
|
||||
/**
|
||||
* 液化命令基类
|
||||
@@ -192,81 +193,6 @@ export class LiquifyCommand extends Command {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图层栅格化命令
|
||||
* 用于将复杂图层栅格化为单一图像,以便进行液化操作
|
||||
*/
|
||||
export class RasterizeForLiquifyCommand extends Command {
|
||||
/**
|
||||
* 创建栅格化命令
|
||||
* @param {Object} options 配置选项
|
||||
* @param {Object} options.canvas Fabric.js画布实例
|
||||
* @param {Object} options.layerManager 图层管理器实例
|
||||
* @param {String} options.layerId 需要栅格化的图层ID
|
||||
*/
|
||||
constructor(options) {
|
||||
super({
|
||||
name: options.name || "栅格化图层",
|
||||
description: options.description || "将图层栅格化为单一图像以便液化操作",
|
||||
});
|
||||
|
||||
this.canvas = options.canvas;
|
||||
this.layerManager = options.layerManager;
|
||||
this.layerId = options.layerId;
|
||||
this.originalLayer = null;
|
||||
this.rasterizedImageObj = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行栅格化操作
|
||||
* @returns {Promise<Object>} 栅格化后的图像对象
|
||||
*/
|
||||
async execute() {
|
||||
if (!this.canvas || !this.layerManager || !this.layerId) {
|
||||
throw new Error("栅格化命令缺少必要参数");
|
||||
}
|
||||
|
||||
// 保存原始图层信息
|
||||
this.originalLayer = this.layerManager.getLayerById(this.layerId);
|
||||
if (!this.originalLayer) {
|
||||
throw new Error(`图层ID不存在: ${this.layerId}`);
|
||||
}
|
||||
|
||||
// 栅格化图层
|
||||
const rasterizedImage = await this.layerManager.rasterizeLayer(
|
||||
this.layerId
|
||||
);
|
||||
if (!rasterizedImage) {
|
||||
throw new Error("栅格化图层失败");
|
||||
}
|
||||
|
||||
this.rasterizedImageObj = rasterizedImage;
|
||||
return rasterizedImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销栅格化操作
|
||||
* 注意:完整撤销栅格化是复杂的,这里提供近似还原
|
||||
* @returns {Promise<boolean>} 撤销结果
|
||||
*/
|
||||
async undo() {
|
||||
if (!this.canvas || !this.layerManager || !this.originalLayer) {
|
||||
throw new Error("无法撤销:缺少必要的状态信息");
|
||||
}
|
||||
|
||||
// 恢复图层为原始状态是复杂的,这里可能需要与LayerManager协作
|
||||
// 这个实现可能需要根据实际的LayerManager功能来调整
|
||||
const restored = await this.layerManager.restoreLayerFromBackup(
|
||||
this.layerId
|
||||
);
|
||||
if (!restored) {
|
||||
console.warn("无法完全还原栅格化前的图层状态");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 液化工具初始化命令
|
||||
* 用于初始化液化工具的状态,不执行实际操作
|
||||
@@ -550,6 +476,405 @@ export class LiquifyResetCommand extends LiquifyCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 液化状态命令 - 最优化版本
|
||||
* 使用引用管理器避免对象引用丢失,支持高性能的状态管理
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 辅助函数:创建液化重置命令
|
||||
* @param {Object} options 配置选项
|
||||
|
||||
Reference in New Issue
Block a user