Files
aida_front/src/component/Canvas/CanvasEditor/managers/LiquifyReferenceManager.js
2025-07-14 01:00:23 +08:00

424 lines
11 KiB
JavaScript

/**
* 液化对象引用管理器
* 专门处理液化操作中的对象引用管理,避免引用丢失问题
*/
export class LiquifyReferenceManager {
constructor() {
// 对象引用池
this.objectRefs = new Map();
// 状态快照池
this.stateSnapshots = new Map();
// ImageData缓存池
this.imageDataCache = new Map();
// 事件监听器备份
this.eventListeners = new Map();
}
/**
* 注册对象到引用管理器
* @param {Object} fabricObject Fabric对象
* @param {String} objectId 对象唯一ID
* @returns {String} 引用ID
*/
registerObject(fabricObject, objectId) {
const refId = objectId || this._generateRefId();
// 保存对象引用
this.objectRefs.set(refId, fabricObject);
// 备份事件监听器
this._backupEventListeners(refId, fabricObject);
// 创建初始状态快照
this._createStateSnapshot(refId, fabricObject);
console.log(`📝 对象已注册到引用管理器: ${refId}`);
return refId;
}
/**
* 获取对象引用
* @param {String} refId 引用ID
* @returns {Object|null} Fabric对象
*/
getObjectRef(refId) {
return this.objectRefs.get(refId) || null;
}
/**
* 更新对象的图像数据,保持引用不变
* @param {String} refId 引用ID
* @param {ImageData} newImageData 新的图像数据
* @returns {Promise<Boolean>} 更新结果
*/
async updateObjectImageData(refId, newImageData) {
const fabricObject = this.objectRefs.get(refId);
if (!fabricObject || !newImageData) {
throw new Error(`无法更新对象图像数据: 对象不存在或数据无效 (${refId})`);
}
return new Promise((resolve, reject) => {
try {
// 创建临时canvas
const tempCanvas = document.createElement("canvas");
tempCanvas.width = newImageData.width;
tempCanvas.height = newImageData.height;
const tempCtx = tempCanvas.getContext("2d");
tempCtx.putImageData(newImageData, 0, 0);
// 创建新的图像元素
const img = new Image();
img.onload = () => {
try {
// 保存当前状态
const currentState = this._captureObjectState(fabricObject);
// 更新图像源
if (fabricObject.setElement) {
fabricObject.setElement(img);
} else if (fabricObject._element) {
fabricObject._element = img;
fabricObject._originalElement = img;
}
// 恢复非图像属性
this._restoreObjectState(fabricObject, currentState);
// 标记需要重新渲染
fabricObject.dirty = true;
fabricObject.setCoords();
// 更新缓存
this._updateImageDataCache(refId, newImageData);
console.log(`✅ 对象图像数据更新成功: ${refId}`);
resolve(true);
} catch (error) {
console.error("更新对象状态失败:", error);
reject(error);
}
};
img.onerror = () => {
reject(new Error("加载图像数据失败"));
};
img.src = tempCanvas.toDataURL();
} catch (error) {
console.error("创建临时canvas失败:", error);
reject(error);
}
});
}
/**
* 创建对象状态快照
* @param {String} refId 引用ID
* @param {String} snapshotId 快照ID
* @returns {String} 快照ID
*/
createSnapshot(refId, snapshotId = null) {
const fabricObject = this.objectRefs.get(refId);
if (!fabricObject) {
throw new Error(`无法创建快照: 对象不存在 (${refId})`);
}
const snapId = snapshotId || `${refId}_${Date.now()}`;
const snapshot = this._createStateSnapshot(snapId, fabricObject);
console.log(`📸 已创建对象快照: ${snapId}`);
return snapId;
}
/**
* 恢复对象到指定快照状态
* @param {String} refId 引用ID
* @param {String} snapshotId 快照ID
* @returns {Promise<Boolean>} 恢复结果
*/
async restoreFromSnapshot(refId, snapshotId) {
const fabricObject = this.objectRefs.get(refId);
const snapshot = this.stateSnapshots.get(snapshotId);
if (!fabricObject || !snapshot) {
throw new Error(`无法恢复快照: 对象或快照不存在 (${refId}, ${snapshotId})`);
}
// 恢复图像数据
if (snapshot.imageData) {
await this.updateObjectImageData(refId, snapshot.imageData);
}
// 恢复对象属性
if (snapshot.properties) {
this._restoreObjectState(fabricObject, snapshot.properties);
}
// 恢复事件监听器
if (snapshot.eventListeners) {
this._restoreEventListeners(refId, fabricObject, snapshot.eventListeners);
}
console.log(`🔄 对象快照恢复成功: ${refId} -> ${snapshotId}`);
return true;
}
/**
* 批量更新多个对象
* @param {Array} updates 更新列表 [{refId, imageData}, ...]
* @returns {Promise<Array>} 更新结果
*/
async batchUpdate(updates) {
const results = [];
for (const update of updates) {
try {
const result = await this.updateObjectImageData(update.refId, update.imageData);
results.push({ refId: update.refId, success: true, result });
} catch (error) {
console.error(`批量更新失败 ${update.refId}:`, error);
results.push({
refId: update.refId,
success: false,
error: error.message,
});
}
}
return results;
}
/**
* 清理不再使用的引用
* @param {String} refId 引用ID
*/
cleanup(refId) {
this.objectRefs.delete(refId);
this.stateSnapshots.delete(refId);
this.imageDataCache.delete(refId);
this.eventListeners.delete(refId);
console.log(`🗑️ 已清理对象引用: ${refId}`);
}
/**
* 清理所有引用
*/
cleanupAll() {
this.objectRefs.clear();
this.stateSnapshots.clear();
this.imageDataCache.clear();
this.eventListeners.clear();
console.log("🗑️ 已清理所有对象引用");
}
/**
* 获取内存使用统计
* @returns {Object} 统计信息
*/
getMemoryStats() {
return {
objectRefs: this.objectRefs.size,
stateSnapshots: this.stateSnapshots.size,
imageDataCache: this.imageDataCache.size,
eventListeners: this.eventListeners.size,
totalMemoryUsage: this._calculateMemoryUsage(),
};
}
// 私有方法
/**
* 生成引用ID
* @returns {String} 引用ID
* @private
*/
_generateRefId() {
return `ref_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
/**
* 备份事件监听器
* @param {String} refId 引用ID
* @param {Object} fabricObject Fabric对象
* @private
*/
_backupEventListeners(refId, fabricObject) {
const listeners = {};
// 备份常见的事件监听器
const eventTypes = ["mousedown", "mouseup", "mousemove", "mouseout", "mouseover"];
eventTypes.forEach((eventType) => {
if (fabricObject.__eventListeners && fabricObject.__eventListeners[eventType]) {
listeners[eventType] = [...fabricObject.__eventListeners[eventType]];
}
});
this.eventListeners.set(refId, listeners);
}
/**
* 恢复事件监听器
* @param {String} refId 引用ID
* @param {Object} fabricObject Fabric对象
* @param {Object} listeners 监听器备份
* @private
*/
_restoreEventListeners(refId, fabricObject, listeners) {
Object.keys(listeners).forEach((eventType) => {
if (listeners[eventType] && listeners[eventType].length > 0) {
listeners[eventType].forEach((listener) => {
fabricObject.on(eventType, listener);
});
}
});
}
/**
* 创建状态快照
* @param {String} snapId 快照ID
* @param {Object} fabricObject Fabric对象
* @returns {Object} 快照数据
* @private
*/
_createStateSnapshot(snapId, fabricObject) {
const snapshot = {
timestamp: Date.now(),
properties: this._captureObjectState(fabricObject),
imageData: this._captureImageData(fabricObject),
eventListeners: this.eventListeners.get(snapId.split("_")[0]) || {},
};
this.stateSnapshots.set(snapId, snapshot);
return snapshot;
}
/**
* 捕获对象状态
* @param {Object} fabricObject Fabric对象
* @returns {Object} 对象状态
* @private
*/
_captureObjectState(fabricObject) {
return {
left: fabricObject.left,
top: fabricObject.top,
scaleX: fabricObject.scaleX,
scaleY: fabricObject.scaleY,
angle: fabricObject.angle,
flipX: fabricObject.flipX,
flipY: fabricObject.flipY,
opacity: fabricObject.opacity,
visible: fabricObject.visible,
selectable: fabricObject.selectable,
evented: fabricObject.evented,
id: fabricObject.id,
layerId: fabricObject.layerId,
customData: fabricObject.customData ? { ...fabricObject.customData } : {},
filters: fabricObject.filters ? [...fabricObject.filters] : [],
};
}
/**
* 恢复对象状态
* @param {Object} fabricObject Fabric对象
* @param {Object} state 状态数据
* @private
*/
_restoreObjectState(fabricObject, state) {
if (!state) return;
fabricObject.set({
left: state.left,
top: state.top,
scaleX: state.scaleX,
scaleY: state.scaleY,
angle: state.angle,
flipX: state.flipX,
flipY: state.flipY,
opacity: state.opacity,
visible: state.visible,
selectable: state.selectable,
evented: state.evented,
});
// 恢复自定义属性
if (state.customData) {
fabricObject.customData = { ...state.customData };
}
// 恢复滤镜
if (state.filters && state.filters.length > 0) {
fabricObject.filters = [...state.filters];
fabricObject.applyFilters();
}
}
/**
* 捕获图像数据
* @param {Object} fabricObject Fabric对象
* @returns {ImageData|null} 图像数据
* @private
*/
_captureImageData(fabricObject) {
try {
if (fabricObject._element && fabricObject._element.width && fabricObject._element.height) {
const canvas = document.createElement("canvas");
canvas.width = fabricObject._element.width;
canvas.height = fabricObject._element.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(fabricObject._element, 0, 0);
return ctx.getImageData(0, 0, canvas.width, canvas.height);
}
} catch (error) {
console.warn("无法捕获图像数据:", error);
}
return null;
}
/**
* 更新图像数据缓存
* @param {String} refId 引用ID
* @param {ImageData} imageData 图像数据
* @private
*/
_updateImageDataCache(refId, imageData) {
this.imageDataCache.set(refId, {
data: imageData,
timestamp: Date.now(),
width: imageData.width,
height: imageData.height,
});
}
/**
* 计算内存使用量(近似值)
* @returns {Number} 内存使用量(字节)
* @private
*/
_calculateMemoryUsage() {
let totalBytes = 0;
// 计算ImageData缓存大小
this.imageDataCache.forEach((cache) => {
totalBytes += cache.width * cache.height * 4; // RGBA = 4 bytes per pixel
});
return totalBytes;
}
}
/**
* 创建单例引用管理器
*/
let liquifyReferenceManagerInstance = null;
export function getLiquifyReferenceManager() {
if (!liquifyReferenceManagerInstance) {
liquifyReferenceManagerInstance = new LiquifyReferenceManager();
}
return liquifyReferenceManagerInstance;
}