/** * 液化实时更新器 * 负责高效地更新液化效果到画布上,避免频繁创建fabric对象导致的性能问题 */ export class LiquifyRealTimeUpdater { constructor(canvas, options = {}) { this.canvas = canvas; this.targetObject = null; this.isUpdating = false; this.updateQueue = []; this.lastUpdateTime = 0; this.currImage = options.currImage || { value: null }; // 配置选项 this.config = { throttleTime: options.throttleTime || 16, // 60fps maxQueueSize: options.maxQueueSize || 5, useDirectUpdate: options.useDirectUpdate !== false, // 默认启用直接更新 imageQuality: options.imageQuality || 1.0, // 图像质量 (0.1-1.0) skipRenderDuringDrag: options.skipRenderDuringDrag || false, // 拖拽时跳过渲染 }; // 临时canvas用于快速渲染 this.tempCanvas = document.createElement("canvas"); this.tempCtx = this.tempCanvas.getContext("2d"); // 高质量canvas用于最终输出 this.highQualityCanvas = document.createElement("canvas"); this.highQualityCtx = this.highQualityCanvas.getContext("2d"); // 当前缓存的图像数据 this.cachedDataURL = null; this.pendingImageData = null; this.renderingScheduled = false; // 优化Canvas画布渲染设置 this.canvas.renderOnAddRemove = false; // 禁用自动渲染 this.canvas.skipOffscreen = true; // 跳过离屏元素渲染 } /** * 设置目标对象 * @param {Object} fabricObject fabric图像对象 */ setTargetObject(fabricObject) { this.targetObject = fabricObject; if (fabricObject && fabricObject._element) { // 设置临时canvas尺寸 this.tempCanvas.width = fabricObject.width; this.tempCanvas.height = fabricObject.height; // 设置高质量canvas尺寸 this.highQualityCanvas.width = fabricObject.width; this.highQualityCanvas.height = fabricObject.height; // 配置高质量渲染上下文 this.highQualityCtx.imageSmoothingEnabled = true; this.highQualityCtx.imageSmoothingQuality = "high"; // 配置临时canvas上下文(快速渲染) this.tempCtx.imageSmoothingEnabled = false; // 快速模式关闭平滑 } } /** * 实时更新图像数据到画布 * @param {ImageData} imageData 新的图像数据 * @param {Boolean} isDrawing 是否正在绘制(拖拽过程中) * @returns {Promise} 更新完成的Promise */ async updateImage(imageData, isDrawing = false) { if (!this.targetObject || !imageData) { return; } // 节流控制 const now = Date.now(); if (now - this.lastUpdateTime < this.config.throttleTime && isDrawing) { // 在绘制过程中进行节流,缓存最新的图像数据 this.pendingImageData = imageData; return; } this.lastUpdateTime = now; if (isDrawing && this.config.useDirectUpdate) { // 拖拽过程中使用快速更新(降低质量以提高性能) this._fastUpdate(imageData); } else { // 拖拽结束后使用完整更新(最高质量) await this._fullUpdate(imageData); } } /** * 智能图像质量更新 * 根据图像尺寸和设备性能动态调整质量 * @param {ImageData} imageData 图像数据 * @param {Boolean} isDrawing 是否正在绘制 * @private */ _getOptimalQuality(imageData, isDrawing) { const pixelCount = imageData.width * imageData.height; if (isDrawing) { // 拖拽时根据图像大小调整质量 if (pixelCount > 1000000) { // 大于1M像素 return 0.7; } else if (pixelCount > 500000) { // 大于500K像素 return 0.8; } else { return 0.9; } } else { // 拖拽结束时始终使用最高质量 return 1.0; } } /** * 快速更新 - 直接修改现有对象的图像源 * @param {ImageData} imageData 图像数据 * @private */ _fastUpdate(imageData) { if (!this.targetObject || !this.targetObject._element) { return; } try { // 将ImageData渲染到临时canvas(快速模式) this.tempCtx.putImageData(imageData, 0, 0); // 获取智能质量设置 const quality = this._getOptimalQuality(imageData, true); // 直接更新fabric对象的图像源(使用PNG格式保持质量) const targetElement = this.targetObject._element; // 方案1: 直接设置src属性(最高性能) const dataURL = this.tempCanvas.toDataURL("image/png", quality); if (targetElement.src !== dataURL) { targetElement.src = dataURL; // 关键优化:直接设置fabric对象为脏状态,但不立即渲染 // this.targetObject.dirty = false; // 标记为不需要立即渲染 // this.canvas.renderOnAddRemove = true; // 恢复自动渲染 // this.renderingScheduled = false; // 重置渲染调度状态 this?.scheduleRender?.(); // 调度一次渲染 // 使用requestAnimationFrame进行批量渲染优化 // if (!this.renderingScheduled && !this.config.skipRenderDuringDrag) { // this.renderingScheduled = true; // requestIdleCallback(() => { // this.canvas.renderAll(); // this.renderingScheduled = false; // }); // } } else { console.warn( "=================快速更新液化效果时,图像数据未变化,跳过更新" ); } } catch (error) { console.error("快速更新液化效果失败:", error); } } getImageData(imageData) { // 使用高质量canvas进行最终渲染 this.highQualityCtx.putImageData(imageData, 0, 0); // 生成高质量DataURL(PNG格式,最大质量) const dataURL = this.highQualityCanvas.toDataURL("image/png", 1.0); return dataURL; } /** * 完整更新 - 创建新的fabric对象 * @param {ImageData} imageData 图像数据 * @private */ async _fullUpdate(imageData) { return new Promise((resolve, reject) => { try { // 使用高质量canvas进行最终渲染 this.highQualityCtx.putImageData(imageData, 0, 0); // 生成高质量DataURL(PNG格式,最大质量) const dataURL = this.highQualityCanvas.toDataURL("image/png", 1.0); // 如果DataURL没有变化,跳过更新 if (this.cachedDataURL === dataURL) { resolve(); return; } this.cachedDataURL = dataURL; // 创建新的fabric图像对象,保持最高质量 fabric.Image.fromURL( dataURL, (newImg) => { try { if (!this.targetObject) { console.warn("目标对象为空,跳过更新"); resolve(); return; } // 保存原对象信息用于智能查找 const originalObjId = this.targetObject.id; const originalObjLayerId = this.targetObject.layerId; // 保留原对象的所有变换属性 const originalObj = this.targetObject; newImg.set({ left: originalObj.left, top: originalObj.top, scaleX: originalObj.scaleX, scaleY: originalObj.scaleY, angle: originalObj.angle, flipX: originalObj.flipX, flipY: originalObj.flipY, opacity: originalObj.opacity, originX: originalObj.originX, originY: originalObj.originY, id: originalObj.id, name: originalObj.name, layerId: originalObj.layerId, selected: false, evented: originalObj.evented, }); // 临时禁用画布自动渲染 const oldRenderOnAddRemove = this.canvas.renderOnAddRemove; this.canvas.renderOnAddRemove = false; // 智能查找和替换canvas上的对象 const allObjects = this.canvas.getObjects(); let targetIndex = allObjects.indexOf(originalObj); // 如果直接查找失败,尝试通过ID查找 if (targetIndex === -1 && originalObjId) { targetIndex = allObjects.findIndex( (obj) => obj.id === originalObjId ); if (targetIndex !== -1) { console.log(`通过ID找到目标对象: ${originalObjId}`); // 更新目标对象引用 this.targetObject = allObjects[targetIndex]; } } // 如果通过ID查找仍然失败,尝试通过图层ID查找 if (targetIndex === -1 && originalObjLayerId) { targetIndex = allObjects.findIndex( (obj) => obj.layerId === originalObjLayerId ); if (targetIndex !== -1) { console.log(`通过图层ID找到目标对象: ${originalObjLayerId}`); // 更新目标对象引用 this.targetObject = allObjects[targetIndex]; } } if (targetIndex !== -1) { // 找到目标对象,执行替换 this.canvas.remove(this.targetObject); this.canvas.insertAt(newImg, targetIndex); // 恢复自动渲染设置 this.canvas.renderOnAddRemove = oldRenderOnAddRemove; // 更新目标对象引用 this.targetObject = newImg; // 一次性重新渲染画布 this.canvas.renderAll(); console.log(`✅ 液化对象更新成功,位置: ${targetIndex}`); resolve(newImg); } else { // 如果在画布中找不到对象,可能对象已被移除或引用已更新 console.warn( "在画布中找不到目标对象,可能已被其他操作移除或替换" ); // 恢复自动渲染设置 this.canvas.renderOnAddRemove = oldRenderOnAddRemove; // 尝试添加新对象到画布末尾 this.canvas.add(newImg); this.targetObject = newImg; this.canvas.renderAll(); console.log("🔄 已将新对象添加到画布末尾"); resolve(newImg); } } catch (error) { // 恢复自动渲染设置 this.canvas.renderOnAddRemove = oldRenderOnAddRemove; console.error("更新fabric对象时出错:", error); reject(error); } }, { crossOrigin: "anonymous" } ); // 确保跨域支持 } catch (error) { console.error("完整更新过程出错:", error); reject(error); } }); } /** * 处理待处理的图像数据 * 在拖拽结束后调用,处理可能积压的更新 */ async processPendingUpdates() { if (this.pendingImageData && !this.isUpdating) { this.isUpdating = true; try { await this._fullUpdate(this.pendingImageData); this.pendingImageData = null; } catch (error) { console.error("处理待处理更新失败:", error); } finally { this.isUpdating = false; } } } /** * 清理资源 */ dispose() { this.targetObject = null; this.cachedDataURL = null; this.pendingImageData = null; this.updateQueue.length = 0; // 清理临时canvas if (this.tempCanvas) { this.tempCanvas.width = 0; this.tempCanvas.height = 0; this.tempCanvas = null; this.tempCtx = null; } } /** * 获取当前目标对象 * @returns {Object} 当前的fabric对象 */ getTargetObject() { return this.targetObject; } /** * 强制进行完整更新 * @param {ImageData} imageData 图像数据 */ async forceFullUpdate(imageData) { return this._fullUpdate(imageData); } /** * 启用拖拽模式 - 暂停渲染以提高性能 */ enableDragMode() { this.config.skipRenderDuringDrag = true; this.canvas.renderOnAddRemove = false; console.log("🚀 启用拖拽优化模式"); } /** * 禁用拖拽模式 - 恢复正常渲染 */ disableDragMode() { this.config.skipRenderDuringDrag = false; this.canvas.renderOnAddRemove = true; // 执行一次完整渲染 this.canvas.renderAll(); console.log("✅ 恢复正常渲染模式"); } /** * 获取当前目标对象 */ getTargetObject() { return this.targetObject; } /** * 设置图像质量 * @param {Number} quality 质量值 (0.1-1.0) */ setImageQuality(quality) { this.config.imageQuality = Math.max(0.1, Math.min(1.0, quality)); } /** * 优化的批量渲染方法 */ scheduleRender() { if (!this.renderingScheduled) { this.renderingScheduled = true; requestAnimationFrame(() => { this.canvas.renderAll(); this.renderingScheduled = false; }); } } /** * 清理资源 */ dispose() { // 恢复canvas设置 this.canvas.renderOnAddRemove = true; // 清理缓存 this.cachedDataURL = null; this.pendingImageData = null; // 清理canvas if (this.tempCanvas) { this.tempCanvas.width = 0; this.tempCanvas.height = 0; } if (this.highQualityCanvas) { this.highQualityCanvas.width = 0; this.highQualityCanvas.height = 0; } console.log("🧹 液化实时更新器资源已清理"); } }