/** * 液化对象引用管理器 * 专门处理液化操作中的对象引用管理,避免引用丢失问题 */ 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} 更新结果 */ 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} 恢复结果 */ 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} 更新结果 */ 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; }