Files
aida_front/src/component/Canvas/CanvasEditor/managers/liquify/LiquifyRealTimeUpdater.js

444 lines
14 KiB
JavaScript
Raw Normal View History

2025-06-18 11:05:23 +08:00
/**
* 液化实时更新器
* 负责高效地更新液化效果到画布上避免频繁创建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) => {
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,
});
// 临时禁用画布自动渲染
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("🧹 液化实时更新器资源已清理");
}
}