画布开发合并:选择状态消失,填充背景实时更新,图层层级关系,新增画布名字递增

This commit is contained in:
李志鹏
2025-11-07 16:52:42 +08:00
parent 2899f4d6fe
commit a10f07a772
13 changed files with 850 additions and 107 deletions

View File

@@ -1,5 +1,7 @@
import { Command, FunctionCommand } from "./Command";
import { getLiquifyReferenceManager } from "../managers/LiquifyReferenceManager";
import { optimizeCanvasRendering } from "../utils/helper";
import { fabric } from "fabric-with-all";
/**
* 液化命令基类
@@ -59,9 +61,7 @@ export class LiquifyCommand extends Command {
// 更新画布上的对象
await this._updateObjectWithResult();
// 刷新Canvas
this.canvas.renderAll();
// 注意_updateObjectWithResult 内部已使用 optimizeCanvasRendering会自动渲染
return this.resultData;
}
@@ -130,6 +130,7 @@ export class LiquifyCommand extends Command {
/**
* 使用变形结果更新对象
* 优化:直接使用 setElement 更新现有对象,避免创建新对象和替换操作
* @private
*/
async _updateObjectWithResult() {
@@ -142,48 +143,103 @@ export class LiquifyCommand extends Command {
const tempCtx = tempCanvas.getContext("2d");
tempCtx.putImageData(this.resultData, 0, 0);
console.log("临时Canvas创建成功 _updateObjectWithResult", this.resultData);
// 更新Fabric图像
await new Promise((resolve) => {
fabric.Image.fromURL(tempCanvas.toDataURL(), (img) => {
// 保留原对象的属性
img.set({
left: this.targetObject.left,
top: this.targetObject.top,
scaleX: this.targetObject.scaleX,
scaleY: this.targetObject.scaleY,
angle: this.targetObject.angle,
flipX: this.targetObject.flipX,
flipY: this.targetObject.flipY,
opacity: this.targetObject.opacity,
});
// 替换Canvas上的对象
const index = this.canvas.getObjects().indexOf(this.targetObject);
if (index !== -1) {
this.canvas.remove(this.targetObject);
this.canvas.insertAt(img, index);
this.targetObject = img;
}
// 确保图层引用更新
const layer = this.layerManager.getLayerById(this.targetLayerId);
if (layer) {
if (layer.type === "background" && layer.fabricObject === this.targetObject) {
layer.fabricObject = img;
} else if (layer.fabricObjects) {
const objIndex = layer.fabricObjects.indexOf(this.targetObject);
if (objIndex !== -1) {
layer.fabricObjects[objIndex] = img;
}
}
}
resolve();
});
// 使用优化渲染工具,避免图层闪烁
await optimizeCanvasRendering(this.canvas, async () => {
// 预加载图像元素,确保完全加载后再更新
await this._updateObjectElementDirectly(tempCanvas.toDataURL());
});
return true;
}
/**
* 直接更新对象的图像元素,避免对象替换造成的图层闪烁
* @param {String} imageDataURL 图像数据的 DataURL
* @private
*/
async _updateObjectElementDirectly(imageDataURL) {
return new Promise((resolve, reject) => {
// 创建 HTMLImageElement 并预加载
const imgElement = new Image();
imgElement.onload = () => {
try {
// 保存当前对象状态(非图像属性)
const currentState = {
left: this.targetObject.left,
top: this.targetObject.top,
scaleX: this.targetObject.scaleX,
scaleY: this.targetObject.scaleY,
angle: this.targetObject.angle,
flipX: this.targetObject.flipX,
flipY: this.targetObject.flipY,
opacity: this.targetObject.opacity,
};
// 直接更新现有对象的图像元素,保持对象引用不变
if (this.targetObject.setElement) {
this.targetObject.setElement(imgElement);
} else if (this.targetObject._element) {
this.targetObject._element = imgElement;
this.targetObject._originalElement = imgElement;
}
// 恢复对象属性setElement 可能会重置一些属性)
this.targetObject.set(currentState);
// 标记对象需要重新渲染
this.targetObject.dirty = true;
this.targetObject.setCoords();
resolve();
} catch (error) {
console.error("更新对象元素失败:", error);
reject(error);
}
};
imgElement.onerror = () => {
reject(new Error("图像加载失败"));
};
// 开始加载图像
imgElement.src = imageDataURL;
});
}
/**
* 安全地替换对象,避免图层闪烁(保留作为备用方法)
* @param {Object} newImg 新的fabric图像对象
* @private
*/
_replaceObjectSafely(newImg) {
// 保存旧对象引用,用于更新图层引用
const oldObject = this.targetObject;
// 替换Canvas上的对象
const index = this.canvas.getObjects().indexOf(oldObject);
if (index !== -1) {
// 在禁用自动渲染的情况下,先插入新对象,再移除旧对象
// 这样可以避免中间出现空白因为renderOnAddRemove已被禁用
this.canvas.insertAt(newImg, index);
this.canvas.remove(oldObject);
this.targetObject = newImg;
}
// 确保图层引用更新
const layer = this.layerManager.getLayerById(this.targetLayerId);
if (layer) {
if (layer.type === "background" && layer.fabricObject === oldObject) {
layer.fabricObject = newImg;
} else if (layer.fabricObjects) {
const objIndex = layer.fabricObjects.indexOf(oldObject);
if (objIndex !== -1) {
layer.fabricObjects[objIndex] = newImg;
}
}
}
}
}
/**
@@ -271,8 +327,8 @@ export class LiquifyDeformCommand extends LiquifyCommand {
// 应用变形结果
await this._updateObjectWithImageData(this.afterData);
// 注意_updateObjectWithImageData 内部已使用 optimizeCanvasRendering会自动渲染
this.canvas.renderAll();
return this.afterData;
}
@@ -283,70 +339,70 @@ export class LiquifyDeformCommand extends LiquifyCommand {
// 恢复到变形前的状态
await this._updateObjectWithImageData(this.beforeData);
// 注意_updateObjectWithImageData 内部已使用 optimizeCanvasRendering会自动渲染
this.canvas.renderAll();
return true;
}
/**
* 使用图像数据更新对象
* 优化:直接使用 setElement 更新现有对象,避免创建新对象和替换操作
* @param {ImageData} imageData 图像数据
* @private
*/
async _updateObjectWithImageData(imageData) {
return new Promise((resolve) => {
// 创建临时canvas
const tempCanvas = document.createElement("canvas");
tempCanvas.width = imageData.width;
tempCanvas.height = imageData.height;
const tempCtx = tempCanvas.getContext("2d");
tempCtx.putImageData(imageData, 0, 0);
// 创建临时canvas
const tempCanvas = document.createElement("canvas");
tempCanvas.width = imageData.width;
tempCanvas.height = imageData.height;
const tempCtx = tempCanvas.getContext("2d");
tempCtx.putImageData(imageData, 0, 0);
// 从canvas创建新的fabric图像
fabric.Image.fromURL(tempCanvas.toDataURL(), (img) => {
// 保留原对象的变换属性
img.set({
left: this.targetObject.left,
top: this.targetObject.top,
scaleX: this.targetObject.scaleX,
scaleY: this.targetObject.scaleY,
angle: this.targetObject.angle,
flipX: this.targetObject.flipX,
flipY: this.targetObject.flipY,
opacity: this.targetObject.opacity,
});
// 替换canvas上的对象
const index = this.canvas.getObjects().indexOf(this.targetObject);
if (index !== -1) {
this.canvas.remove(this.targetObject);
this.canvas.insertAt(img, index);
this.targetObject = img;
// 更新图层引用
const layer = this.layerManager.getLayerById(this.targetLayerId);
if (layer) {
if (
layer.type === "background" &&
(layer.fabricObject === this.savedState?.originalObject ||
layer.fabricObject === this.targetObject)
) {
layer.fabricObject = img;
} else if (layer.fabricObjects) {
const objIndex = layer.fabricObjects.findIndex(
(obj) => obj === this.savedState?.originalObject || obj === this.targetObject
);
if (objIndex !== -1) {
layer.fabricObjects[objIndex] = img;
}
}
}
}
resolve();
});
// 使用优化渲染工具,避免图层闪烁
await optimizeCanvasRendering(this.canvas, async () => {
// 直接更新对象元素,避免对象替换
await this._updateObjectElementDirectly(tempCanvas.toDataURL());
});
}
/**
* 安全地替换变形对象,避免图层闪烁(保留作为备用方法)
* @param {Object} newImg 新的fabric图像对象
* @private
*/
_replaceDeformObjectSafely(newImg) {
// 保存旧对象引用,用于更新图层引用
const oldObject = this.targetObject;
// 替换Canvas上的对象
const index = this.canvas.getObjects().indexOf(oldObject);
if (index !== -1) {
// 在禁用自动渲染的情况下,先插入新对象,再移除旧对象
// 这样可以避免中间出现空白
this.canvas.insertAt(newImg, index);
this.canvas.remove(oldObject);
this.targetObject = newImg;
}
// 更新图层引用
const layer = this.layerManager.getLayerById(this.targetLayerId);
if (layer) {
if (
layer.type === "background" &&
(layer.fabricObject === this.savedState?.originalObject ||
layer.fabricObject === oldObject)
) {
layer.fabricObject = newImg;
} else if (layer.fabricObjects) {
const objIndex = layer.fabricObjects.findIndex(
(obj) => obj === this.savedState?.originalObject || obj === oldObject
);
if (objIndex !== -1) {
layer.fabricObjects[objIndex] = newImg;
}
}
}
}
}
/**