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

428 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 液化实时更新器
* 负责高效地更新液化效果到画布上避免频繁创建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);
// 生成高质量DataURLPNG格式最大质量
const dataURL = this.highQualityCanvas.toDataURL("image/png", 1.0);
return dataURL;
}
/**
* 完整更新 - 创建新的fabric对象
* @param {ImageData} imageData 图像数据
* @private
*/
async _fullUpdate(imageData) {
return new Promise((resolve, reject) => {
// 临时禁用画布自动渲染
const oldRenderOnAddRemove = this.canvas.renderOnAddRemove;
try {
// 使用高质量canvas进行最终渲染
this.highQualityCtx.putImageData(imageData, 0, 0);
// 生成高质量DataURLPNG格式最大质量
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,
});
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;
}
}
}
/**
* 获取当前目标对象
* @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("✅ 恢复正常渲染模式");
}
/**
* 设置图像质量
* @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() {
// 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;
// }
// }
/**
* 清理资源
*/
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("🧹 液化实时更新器资源已清理");
}
}