bugfix: 液化工具闪烁
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Command, FunctionCommand } from "./Command";
|
||||
import { getLiquifyReferenceManager } from "../managers/LiquifyReferenceManager";
|
||||
import { optimizeCanvasRendering } from "../utils/helper";
|
||||
|
||||
/**
|
||||
* 液化命令基类
|
||||
@@ -59,9 +60,7 @@ export class LiquifyCommand extends Command {
|
||||
|
||||
// 更新画布上的对象
|
||||
await this._updateObjectWithResult();
|
||||
|
||||
// 刷新Canvas
|
||||
this.canvas.renderAll();
|
||||
// 注意:_updateObjectWithResult 内部已使用 optimizeCanvasRendering,会自动渲染
|
||||
|
||||
return this.resultData;
|
||||
}
|
||||
@@ -142,48 +141,82 @@ 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;
|
||||
}
|
||||
|
||||
// 使用优化渲染工具,避免图层闪烁
|
||||
await optimizeCanvasRendering(this.canvas, async () => {
|
||||
// 预先加载图像,确保完全加载后再替换
|
||||
await new Promise((resolve, reject) => {
|
||||
fabric.Image.fromURL(tempCanvas.toDataURL(), (img) => {
|
||||
if (!img) {
|
||||
reject(new Error("图像加载失败"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
resolve();
|
||||
// 保留原对象的属性
|
||||
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,
|
||||
});
|
||||
|
||||
// 确保图像已完全加载(通过检查元素)
|
||||
if (img._element && img._element.complete) {
|
||||
this._replaceObjectSafely(img);
|
||||
resolve();
|
||||
} else {
|
||||
// 如果图像还未加载完成,等待加载
|
||||
img.on("loaded", () => {
|
||||
this._replaceObjectSafely(img);
|
||||
resolve();
|
||||
});
|
||||
img.on("error", (error) => {
|
||||
reject(error || new Error("图像加载失败"));
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地替换对象,避免图层闪烁
|
||||
* @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 +304,8 @@ export class LiquifyDeformCommand extends LiquifyCommand {
|
||||
|
||||
// 应用变形结果
|
||||
await this._updateObjectWithImageData(this.afterData);
|
||||
// 注意:_updateObjectWithImageData 内部已使用 optimizeCanvasRendering,会自动渲染
|
||||
|
||||
this.canvas.renderAll();
|
||||
return this.afterData;
|
||||
}
|
||||
|
||||
@@ -283,8 +316,8 @@ export class LiquifyDeformCommand extends LiquifyCommand {
|
||||
|
||||
// 恢复到变形前的状态
|
||||
await this._updateObjectWithImageData(this.beforeData);
|
||||
// 注意:_updateObjectWithImageData 内部已使用 optimizeCanvasRendering,会自动渲染
|
||||
|
||||
this.canvas.renderAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -294,59 +327,92 @@ export class LiquifyDeformCommand extends LiquifyCommand {
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
// 使用优化渲染工具,避免图层闪烁
|
||||
await optimizeCanvasRendering(this.canvas, async () => {
|
||||
// 预先加载图像,确保完全加载后再替换
|
||||
await new Promise((resolve, reject) => {
|
||||
fabric.Image.fromURL(tempCanvas.toDataURL(), (img) => {
|
||||
if (!img) {
|
||||
reject(new Error("图像加载失败"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
resolve();
|
||||
// 保留原对象的变换属性
|
||||
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,
|
||||
});
|
||||
|
||||
// 确保图像已完全加载
|
||||
if (img._element && img._element.complete) {
|
||||
this._replaceDeformObjectSafely(img);
|
||||
resolve();
|
||||
} else {
|
||||
// 如果图像还未加载完成,等待加载
|
||||
img.on("loaded", () => {
|
||||
this._replaceDeformObjectSafely(img);
|
||||
resolve();
|
||||
});
|
||||
img.on("error", (error) => {
|
||||
reject(error || new Error("图像加载失败"));
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地替换变形对象,避免图层闪烁
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user