bugfix: 液化工具闪烁
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { Command, FunctionCommand } from "./Command";
|
import { Command, FunctionCommand } from "./Command";
|
||||||
import { getLiquifyReferenceManager } from "../managers/LiquifyReferenceManager";
|
import { getLiquifyReferenceManager } from "../managers/LiquifyReferenceManager";
|
||||||
|
import { optimizeCanvasRendering } from "../utils/helper";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 液化命令基类
|
* 液化命令基类
|
||||||
@@ -59,9 +60,7 @@ export class LiquifyCommand extends Command {
|
|||||||
|
|
||||||
// 更新画布上的对象
|
// 更新画布上的对象
|
||||||
await this._updateObjectWithResult();
|
await this._updateObjectWithResult();
|
||||||
|
// 注意:_updateObjectWithResult 内部已使用 optimizeCanvasRendering,会自动渲染
|
||||||
// 刷新Canvas
|
|
||||||
this.canvas.renderAll();
|
|
||||||
|
|
||||||
return this.resultData;
|
return this.resultData;
|
||||||
}
|
}
|
||||||
@@ -142,48 +141,82 @@ export class LiquifyCommand extends Command {
|
|||||||
const tempCtx = tempCanvas.getContext("2d");
|
const tempCtx = tempCanvas.getContext("2d");
|
||||||
tempCtx.putImageData(this.resultData, 0, 0);
|
tempCtx.putImageData(this.resultData, 0, 0);
|
||||||
console.log("临时Canvas创建成功 _updateObjectWithResult", this.resultData);
|
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);
|
await optimizeCanvasRendering(this.canvas, async () => {
|
||||||
if (index !== -1) {
|
// 预先加载图像,确保完全加载后再替换
|
||||||
this.canvas.remove(this.targetObject);
|
await new Promise((resolve, reject) => {
|
||||||
this.canvas.insertAt(img, index);
|
fabric.Image.fromURL(tempCanvas.toDataURL(), (img) => {
|
||||||
this.targetObject = img;
|
if (!img) {
|
||||||
}
|
reject(new Error("图像加载失败"));
|
||||||
|
return;
|
||||||
// 确保图层引用更新
|
|
||||||
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();
|
// 保留原对象的属性
|
||||||
|
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;
|
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);
|
await this._updateObjectWithImageData(this.afterData);
|
||||||
|
// 注意:_updateObjectWithImageData 内部已使用 optimizeCanvasRendering,会自动渲染
|
||||||
|
|
||||||
this.canvas.renderAll();
|
|
||||||
return this.afterData;
|
return this.afterData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,8 +316,8 @@ export class LiquifyDeformCommand extends LiquifyCommand {
|
|||||||
|
|
||||||
// 恢复到变形前的状态
|
// 恢复到变形前的状态
|
||||||
await this._updateObjectWithImageData(this.beforeData);
|
await this._updateObjectWithImageData(this.beforeData);
|
||||||
|
// 注意:_updateObjectWithImageData 内部已使用 optimizeCanvasRendering,会自动渲染
|
||||||
|
|
||||||
this.canvas.renderAll();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,59 +327,92 @@ export class LiquifyDeformCommand extends LiquifyCommand {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
async _updateObjectWithImageData(imageData) {
|
async _updateObjectWithImageData(imageData) {
|
||||||
return new Promise((resolve) => {
|
// 创建临时canvas
|
||||||
// 创建临时canvas
|
const tempCanvas = document.createElement("canvas");
|
||||||
const tempCanvas = document.createElement("canvas");
|
tempCanvas.width = imageData.width;
|
||||||
tempCanvas.width = imageData.width;
|
tempCanvas.height = imageData.height;
|
||||||
tempCanvas.height = imageData.height;
|
const tempCtx = tempCanvas.getContext("2d");
|
||||||
const tempCtx = tempCanvas.getContext("2d");
|
tempCtx.putImageData(imageData, 0, 0);
|
||||||
tempCtx.putImageData(imageData, 0, 0);
|
|
||||||
|
|
||||||
// 从canvas创建新的fabric图像
|
// 使用优化渲染工具,避免图层闪烁
|
||||||
fabric.Image.fromURL(tempCanvas.toDataURL(), (img) => {
|
await optimizeCanvasRendering(this.canvas, async () => {
|
||||||
// 保留原对象的变换属性
|
// 预先加载图像,确保完全加载后再替换
|
||||||
img.set({
|
await new Promise((resolve, reject) => {
|
||||||
left: this.targetObject.left,
|
fabric.Image.fromURL(tempCanvas.toDataURL(), (img) => {
|
||||||
top: this.targetObject.top,
|
if (!img) {
|
||||||
scaleX: this.targetObject.scaleX,
|
reject(new Error("图像加载失败"));
|
||||||
scaleY: this.targetObject.scaleY,
|
return;
|
||||||
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();
|
// 保留原对象的变换属性
|
||||||
|
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