合并画布代码

This commit is contained in:
X1627315083
2025-06-18 11:05:23 +08:00
parent 903c0ebdf5
commit 9c7fae36eb
118 changed files with 23633 additions and 8201 deletions

View File

@@ -1,6 +1,11 @@
import { OperationType } from "../utils/layerHelper.js";
import {
findObjectById,
generateId,
optimizeCanvasRendering,
} from "../utils/helper.js";
import { LayerType, OperationType } from "../utils/layerHelper.js";
import { Command, CompositeCommand } from "./Command.js";
//import { fabric } from "fabric-with-all";
import { fabric } from "fabric-with-all";
/**
* 批量初始化红绿图模式命令
@@ -29,100 +34,134 @@ export class BatchInitializeRedGreenModeCommand extends Command {
this.originalNormalObjects = null;
this.originalNormalOpacities = new Map();
this.originalToolState = null;
this.originalActiveLayerId = null;
// 存储加载的图片对象
this.clothingImage = null;
this.redGreenImage = null;
// 存储新创建的图层ID
this.newEmptyLayerId = null;
}
async execute() {
try {
// 禁用画布渲染以避免闪烁
this.canvas.renderOnAddRemove = false;
await optimizeCanvasRendering(this.canvas, async () => {
// 1. 设置画布背景为白色
this.originalCanvasBackground = this.canvas.backgroundColor;
this.canvas.setBackgroundColor("#ffffff", () => {});
// 1. 设置画布背景为白色
this.originalCanvasBackground = this.canvas.backgroundColor;
this.canvas.setBackgroundColor('#ffffff', () => {});
// 2. 查找图层结构
const layers = this.layerManager.layers?.value || [];
const backgroundLayer = layers.find((layer) => layer.isBackground);
const fixedLayer = layers.find((layer) => layer.isFixed);
const normalLayers = layers.filter(
(layer) => !layer.isBackground && !layer.isFixed
);
// 2. 查找图层结构
const layers = this.layerManager.layers?.value || [];
const backgroundLayer = layers.find((layer) => layer.isBackground);
const fixedLayer = layers.find((layer) => layer.isFixed);
const normalLayers = layers.filter(
(layer) => !layer.isBackground && !layer.isFixed
);
if (!backgroundLayer || !fixedLayer || normalLayers.length === 0) {
throw new Error("缺少必要的图层结构");
}
const normalLayer = normalLayers[0]; // 使用第一个普通图层
// 3. 保存原始状态
this.originalBackgroundObject = backgroundLayer.fabricObject ? {
...backgroundLayer.fabricObject.toObject(),
ref: backgroundLayer.fabricObject
} : null;
this.originalFixedObjects = fixedLayer.fabricObject
? [fixedLayer.fabricObject]
: [];
this.originalNormalObjects = normalLayer.fabricObjects
? [...normalLayer.fabricObjects]
: [];
// 保存普通图层透明度
normalLayers.forEach((layer) => {
this.originalNormalOpacities.set(layer.id, layer.opacity || 1);
if (layer.fabricObjects) {
layer.fabricObjects.forEach((obj) => {
this.originalNormalOpacities.set(
`${layer.id}_${obj.id || "unknown"}`,
obj.opacity || 1
);
});
if (!backgroundLayer || !fixedLayer || normalLayers.length === 0) {
throw new Error("缺少必要的图层结构");
}
});
// 保存工具状态
if (this.toolManager) {
this.originalToolState = {
currentTool: this.toolManager.getCurrentTool(),
isRedGreenMode: this.toolManager.isRedGreenMode,
};
}
const normalLayer = normalLayers[0]; // 使用第一个普通图层
// 4. 确保背景图层大小正确
await this._setupBackgroundLayer(backgroundLayer);
// 3. 保存原始状态
this.originalBackgroundObject = backgroundLayer.fabricObject
? {
...backgroundLayer.fabricObject.toObject(),
ref: backgroundLayer.fabricObject,
}
: null;
// 5. 并行加载两个图片
const [clothingImg, redGreenImg] = await Promise.all([
this._loadImage(this.clothingImageUrl),
this._loadImage(this.redGreenImageUrl)
]);
this.originalFixedObjects = fixedLayer.fabricObject
? [fixedLayer.fabricObject]
: [];
// 6. 设置衣服底图到固定图层
await this._setupClothingImage(clothingImg, fixedLayer);
this.originalNormalObjects = normalLayer.fabricObjects
? [...normalLayer.fabricObjects]
: [];
// 7. 设置红绿图到普通图层,位置和大小与衣服底图一致
await this._setupRedGreenImage(redGreenImg, normalLayer, this.clothingImage);
// 保存当前活动图层ID
this.originalActiveLayerId = this.layerManager.getActiveLayerId();
// 8. 设置普通图层透明度
this._setupNormalLayerOpacity(normalLayers);
// 保存普通图层透明度
normalLayers.forEach((layer) => {
this.originalNormalOpacities.set(layer.id, layer.opacity || 1);
if (layer.fabricObjects) {
layer.fabricObjects.forEach((obj) => {
this.originalNormalOpacities.set(
`${layer.id}_${obj.id || "unknown"}`,
obj.opacity || 1
);
});
}
});
// 9. 配置工具管理器
this._setupToolManager();
// 保存工具状态
if (this.toolManager) {
this.originalToolState = {
currentTool: this.toolManager.getCurrentTool(),
isRedGreenMode: this.toolManager.isRedGreenMode,
};
}
// 10. 重新启用渲染并执行一次性渲染
this.canvas.renderOnAddRemove = true;
this.canvas.renderAll();
// 5. 并行加载两个图片
const [clothingImg, redGreenImg] = await Promise.all([
this._loadImage(this.clothingImageUrl),
this._loadImage(this.redGreenImageUrl),
]);
console.log("批量红绿图模式初始化完成", {
衣服底图: this.clothingImageUrl,
红绿图: this.redGreenImageUrl,
普通图层透明度: `${Math.round(this.normalLayerOpacity * 100)}%`,
画布背景: "白色",
// 6. 设置衣服底图到固定图层
await this._setupClothingImage(clothingImg, fixedLayer);
// 7. 设置红绿图到普通图层,位置和大小与衣服底图一致
await this._setupRedGreenImage(
redGreenImg,
normalLayer,
this.clothingImage
);
// 4. 确保背景图层大小和衣服地图大小一致
await this._setupBackgroundLayer(backgroundLayer, this.clothingImage);
// 8. 设置普通图层透明度
this._setupNormalLayerOpacity(normalLayers); // 这里不需要在这里设置透明度 由图层统一处理
// 9. 创建新的空白图层并设置为活动图层
this.newEmptyLayerId = await this._createAndActivateEmptyLayer();
// 设置普通图层的裁剪对象为衣服底图
if (this.redGreenImage) {
// const clipPathImg = this.redGreenImage;
// clipPathImg.set({
// absolutePositioned: true,
// });
this.redGreenImage.set({
absolutePositioned: true,
});
const activeLayer = this.layerManager.getActiveLayer();
activeLayer.clippingMask = this.redGreenImage.toObject(["id"]);
activeLayer.opacity = this.normalLayerOpacity;
// activeLayer?.fabricObjects.forEach((obj) => {
// obj.set({
// clipPath: clipPathImg,
// });
// });
}
// 10. 配置工具管理器
this._setupToolManager();
console.log("批量红绿图模式初始化完成", {
衣服底图: this.clothingImageUrl,
红绿图: this.redGreenImageUrl,
普通图层透明度: `${Math.round(this.normalLayerOpacity * 100)}%`,
画布背景: "白色",
新建空图层ID: this.newEmptyLayerId,
});
await this.layerManager.updateLayersObjectsInteractivity(false);
});
return true;
@@ -134,86 +173,125 @@ export class BatchInitializeRedGreenModeCommand extends Command {
}
}
/**
* 创建新的空白图层并设置为活动图层
* @returns {Promise<string>} 新创建的图层ID
* @private
*/
async _createAndActivateEmptyLayer() {
// 创建新的空白图层
const newLayerName = "绘制图层";
const newLayerId = this.layerManager.createLayer(
newLayerName,
LayerType.GROUP,
{
undoable: false,
}
);
// 设置为活动图层
if (newLayerId) {
this.layerManager.setActiveLayer(newLayerId);
}
return newLayerId;
}
async undo() {
try {
// 禁用渲染
this.canvas.renderOnAddRemove = false;
// 1. 恢复画布背景
if (this.originalCanvasBackground !== null) {
this.canvas.setBackgroundColor(this.originalCanvasBackground, () => {});
}
// 2. 恢复图层对象
const layers = this.layerManager.layers?.value || [];
const backgroundLayer = layers.find((layer) => layer.isBackground);
const fixedLayer = layers.find((layer) => layer.isFixed);
const normalLayers = layers.filter(
(layer) => !layer.isBackground && !layer.isFixed
);
// 移除当前添加的对象
if (this.clothingImage) {
this.canvas.remove(this.clothingImage);
}
if (this.redGreenImage) {
this.canvas.remove(this.redGreenImage);
}
// 恢复背景图层
if (backgroundLayer && this.originalBackgroundObject) {
if (this.originalBackgroundObject.ref) {
backgroundLayer.fabricObject = this.originalBackgroundObject.ref;
}
}
// 恢复固定图层
if (fixedLayer) {
fixedLayer.fabricObject = this.originalFixedObjects.length > 0
? this.originalFixedObjects[0]
: null;
if (fixedLayer.fabricObject) {
this.canvas.add(fixedLayer.fabricObject);
}
}
// 恢复普通图层
if (normalLayers.length > 0) {
const normalLayer = normalLayers[0];
normalLayer.fabricObjects = [...this.originalNormalObjects];
this.originalNormalObjects.forEach((obj) => {
this.canvas.add(obj);
});
}
// 3. 恢复透明度
normalLayers.forEach((layer) => {
if (this.originalNormalOpacities.has(layer.id)) {
layer.opacity = this.originalNormalOpacities.get(layer.id);
await optimizeCanvasRendering(this.canvas, async () => {
// 1. 恢复画布背景
if (this.originalCanvasBackground !== null) {
this.canvas.setBackgroundColor(
this.originalCanvasBackground,
() => {}
);
}
if (layer.fabricObjects) {
layer.fabricObjects.forEach((obj) => {
const key = `${layer.id}_${obj.id || "unknown"}`;
if (this.originalNormalOpacities.has(key)) {
obj.opacity = this.originalNormalOpacities.get(key);
}
// 2. 恢复图层对象
const layers = this.layerManager.layers?.value || [];
const backgroundLayer = layers.find((layer) => layer.isBackground);
const fixedLayer = layers.find((layer) => layer.isFixed);
const normalLayers = layers.filter(
(layer) => !layer.isBackground && !layer.isFixed
);
// 移除当前添加的对象
if (this.clothingImage) {
this.canvas.remove(this.clothingImage);
}
if (this.redGreenImage) {
this.canvas.remove(this.redGreenImage);
}
// 移除新创建的空白图层
if (this.newEmptyLayerId) {
const emptyLayerIndex = layers.findIndex(
(layer) => layer.id === this.newEmptyLayerId
);
if (emptyLayerIndex !== -1) {
layers.splice(emptyLayerIndex, 1);
}
}
// 恢复背景图层
if (backgroundLayer && this.originalBackgroundObject) {
if (this.originalBackgroundObject.ref) {
backgroundLayer.fabricObject = this.originalBackgroundObject.ref;
}
}
// 恢复固定图层
if (fixedLayer) {
fixedLayer.fabricObject =
this.originalFixedObjects.length > 0
? this.originalFixedObjects[0]
: null;
if (fixedLayer.fabricObject) {
this.canvas.add(fixedLayer.fabricObject);
}
}
// 恢复普通图层
if (normalLayers.length > 0) {
const normalLayer = normalLayers[0];
normalLayer.fabricObjects = [...this.originalNormalObjects];
this.originalNormalObjects.forEach((obj) => {
this.canvas.add(obj);
});
}
});
// 4. 恢复工具状态
if (this.toolManager && this.originalToolState) {
this.toolManager.isRedGreenMode = this.originalToolState.isRedGreenMode;
if (this.originalToolState.currentTool) {
this.toolManager.setTool(this.originalToolState.currentTool);
// 3. 恢复透明度
normalLayers.forEach((layer) => {
if (this.originalNormalOpacities.has(layer.id)) {
layer.opacity = this.originalNormalOpacities.get(layer.id);
}
if (layer.fabricObjects) {
layer.fabricObjects.forEach((obj) => {
const key = `${layer.id}_${obj.id || "unknown"}`;
if (this.originalNormalOpacities.has(key)) {
obj.opacity = this.originalNormalOpacities.get(key);
}
});
}
});
// 恢复活动图层
if (this.originalActiveLayerId) {
this.layerManager.setActiveLayer(this.originalActiveLayerId);
}
}
// 5. 重新启用渲染
this.canvas.renderOnAddRemove = true;
this.canvas.renderAll();
// 4. 恢复工具状态
if (this.toolManager && this.originalToolState) {
this.toolManager.isRedGreenMode =
this.originalToolState.isRedGreenMode;
if (this.originalToolState.currentTool) {
this.toolManager.setTool(this.originalToolState.currentTool);
}
}
});
return true;
} catch (error) {
@@ -226,35 +304,42 @@ export class BatchInitializeRedGreenModeCommand extends Command {
/**
* 设置背景图层
*/
async _setupBackgroundLayer(backgroundLayer) {
async _setupBackgroundLayer(backgroundLayer, clothingImage) {
let backgroundObject = backgroundLayer.fabricObject;
const { object } = findObjectById(this.canvas, backgroundObject.id);
if (!backgroundObject) {
if (!object) {
// 创建白色背景矩形
backgroundObject = new fabric.Rect({
left: 0,
top: 0,
width: this.canvas.width,
height: this.canvas.height,
fill: "#ffffff",
object = new fabric.Rect({
left: this.canvas.width / 2,
top: this.canvas.height / 2,
width: clothingImage.width,
height: clothingImage.height,
scaleX: clothingImage.scaleX,
scaleY: clothingImage.scaleY,
fill: "transparent", // 确保背景是透明的
selectable: false,
evented: false,
isBackground: true,
layerId: backgroundLayer.id,
layerName: backgroundLayer.name,
originX: "center",
originY: "center",
});
this.canvas.add(backgroundObject);
this.canvas.sendToBack(backgroundObject);
backgroundLayer.fabricObject = backgroundObject;
this.canvas.add(object);
this.canvas.sendToBack(object);
backgroundLayer.fabricObject = object;
} else {
// 更新现有背景对象大小
backgroundObject.set({
width: this.canvas.width,
height: this.canvas.height,
left: 0,
top: 0,
fill: "#ffffff", // 确保背景是白色
object.set({
width: clothingImage.width,
height: clothingImage.height,
scaleX: clothingImage.scaleX,
scaleY: clothingImage.scaleY,
left: this.canvas.width / 2,
top: this.canvas.height / 2,
fill: "transparent", // 确保背景是透明的
});
}
}
@@ -299,6 +384,7 @@ export class BatchInitializeRedGreenModeCommand extends Command {
evented: false,
layerId: fixedLayer.id,
layerName: fixedLayer.name,
id: generateId("clothingImage"),
});
// 清除固定图层原有内容
@@ -332,6 +418,7 @@ export class BatchInitializeRedGreenModeCommand extends Command {
evented: false,
layerId: normalLayer.id,
layerName: normalLayer.name,
id: generateId("redGreenImage"),
});
// 清除普通图层原有内容
@@ -341,6 +428,8 @@ export class BatchInitializeRedGreenModeCommand extends Command {
});
}
// 给img设置裁剪裁剪图为衣服底图
// 添加到画布和普通图层
this.canvas.add(img);
normalLayer.fabricObjects = [img];
@@ -354,13 +443,6 @@ export class BatchInitializeRedGreenModeCommand extends Command {
normalLayers.forEach((layer) => {
// 设置图层透明度
layer.opacity = this.normalLayerOpacity;
// 更新图层中所有对象的透明度
if (layer.fabricObjects) {
layer.fabricObjects.forEach((obj) => {
obj.opacity = this.normalLayerOpacity;
});
}
});
}