Files
aida_front/src/component/Canvas/CanvasEditor/managers/PartManager.js

399 lines
10 KiB
JavaScript
Raw Normal View History

2026-01-14 14:43:43 +08:00
import { fabric } from "fabric-with-all";
import { generateId } from "../utils/helper";
import { OperationType } from "../utils/layerHelper";
import { CreateSelectionCommand } from "../commands/SelectionCommands";
import { ClearSelectionCommand } from "../commands/LassoCutoutCommand";
2026-01-15 13:42:33 +08:00
import addIcon from "@/assets/images/canvas/add.png";
import removeIcon from "@/assets/images/canvas/remove.png";
2026-01-15 17:15:05 +08:00
import { Https } from "@/tool/https";
import store from "@/store";
2026-01-16 15:16:33 +08:00
import { createStaticCanvas } from "../utils/canvasFactory";
import { getObjectAlphaToCanvas } from "../utils/objectHelper";
2026-01-14 14:43:43 +08:00
/**
* 部件选择管理器
*/
export class PartManager {
2026-01-15 13:42:33 +08:00
/**
* 创建部件选择管理器
* @param {Object} options 配置选项
* @param {Object} options.canvas fabric.js画布实例
* @param {Object} options.commandManager 命令管理器实例
* @param {Object} options.canvasManager 画布管理实例
* @param {Object} options.layerManager 图层管理实例
* @param {Object} options.toolManager 工具管理实例
*/
constructor(options = {}) {
this.canvas = options.canvas;
this.commandManager = options.commandManager;
this.layerManager = options.layerManager;
this.canvasManager = options.canvasManager;
this.toolManager = options.toolManager;
2026-01-15 17:15:05 +08:00
this.props = options.props;
2026-01-15 13:42:33 +08:00
// 状态
this.isActive = false;
this.partObject = null; // 当前选区对象
this.partId = "part_selector";
this.defaultCursor = "default";
// 绘制状态
this.drawingObject = null;
this.startPoint = null;
this.partPath = null; // 存储选区路径数据
// 不再直接绑定事件处理函数
this._mouseDownHandler = null;
this._mouseMoveHandler = null;
this._mouseUpHandler = null;
this._keyDownHandler = null;
// 选区相关的工具类型
this.tools = [
OperationType.PART,
OperationType.PART_RECTANGLE,
OperationType.PART_BRUSH,
OperationType.PART_ERASER,
];
2026-01-16 15:16:33 +08:00
this.pointList = []; // 存储点选坐标
2026-01-15 13:42:33 +08:00
// 当前工具
this.activeTool = this.toolManager.activeTool;
}
/**
* 设置当前工具
* @param {String} toolId 工具ID
*/
setCurrentTool(toolId) {
// 检查是否为选区工具
const wasActive = this.isActive;
this.isActive = this.tools.includes(toolId);
// 如果从非选区工具切换到选区工具,初始化事件
if (!wasActive && this.isActive) {
this.initEvents();
2026-01-16 15:16:33 +08:00
this.createPartObject();
2026-01-15 13:42:33 +08:00
}
// 如果从选区工具切换到非选区工具,清理事件和选区
else if (wasActive && !this.isActive) {
this.cleanupEvents();
2026-01-16 15:16:33 +08:00
this.clearPartObject();
2026-01-15 13:42:33 +08:00
}
}
/**
* 初始化选区相关事件
*/
initEvents() {
if (!this.canvas || this._mouseDownHandler) return; // 避免重复初始化
this.defaultCursor = this.canvas.defaultCursor;
// 保存实例引用,用于事件处理函数中
const self = this;
// 鼠标按下事件处理
this._mouseDownHandler = (options) => {
// 如果选区功能未激活,不处理事件
if (!this.isActive) return;
// 阻止事件冒泡,避免与 CanvasEventManager 冲突
options.e.stopPropagation();
switch (this.activeTool.value) {
case OperationType.PART:
this._pointDownkHandler(options);
break;
case OperationType.PART_RECTANGLE:
this._rectangleDownHandler(options);
break;
case OperationType.PART_BRUSH:
this._brushDownHandler(options);
break;
case OperationType.PART_ERASER:
this._eraseDownHandler(options);
break;
default:
break;
}
};
// 鼠标移动事件处理
this._mouseMoveHandler = (options) => {
// 如果选区功能未激活或没有正在绘制的对象,不处理事件
if (!this.isActive) return;
// 阻止事件冒泡
options.e.stopPropagation();
switch (this.activeTool.value) {
case OperationType.PART:
this._pointMoveHandler(options);
break;
case OperationType.PART_RECTANGLE:
this._rectangleMoveHandler(options);
break;
case OperationType.PART_BRUSH:
this._brushMoveHandler(options);
break;
case OperationType.PART_ERASER:
this._eraseMoveHandler(options);
break;
default:
break;
}
};
// 鼠标抬起事件处理
this._mouseUpHandler = (options) => {
// 如果选区功能未激活或没有正在绘制的对象,不处理事件
if (!this.isActive) return;
// 阻止事件冒泡
if (options && options.e) {
options.e.stopPropagation();
}
switch (this.activeTool.value) {
case OperationType.PART:
this._pointUpHandler(options);
break;
case OperationType.PART_RECTANGLE:
this._rectangleUpHandler(options);
break;
case OperationType.PART_BRUSH:
this._brushUpHandler(options);
break;
case OperationType.PART_ERASER:
this._eraseUpHandler(options);
break;
default:
break;
}
};
// 键盘事件处理
this._keyDownHandler = (event) => {
// 只在选区功能激活时处理键盘事件
if (!this.isActive) return;
};
// 添加事件监听
this.canvas.on("mouse:down", this._mouseDownHandler);
this.canvas.on("mouse:move", this._mouseMoveHandler);
this.canvas.on("mouse:up", this._mouseUpHandler);
// 添加键盘事件监听
document.addEventListener("keydown", this._keyDownHandler);
}
/**
* 清理事件监听
*/
cleanupEvents() {
if (!this.canvas) return;
// 移除事件监听
if (this._mouseDownHandler) {
this.canvas.off("mouse:down", this._mouseDownHandler);
this._mouseDownHandler = null;
}
if (this._mouseMoveHandler) {
this.canvas.off("mouse:move", this._mouseMoveHandler);
this._mouseMoveHandler = null;
}
if (this._mouseUpHandler) {
this.canvas.off("mouse:up", this._mouseUpHandler);
this._mouseUpHandler = null;
}
if (this._keyDownHandler) {
document.removeEventListener("keydown", this._keyDownHandler);
this._keyDownHandler = null;
}
}
// 点选工具模式下点击事件处理
_pointDownkHandler(options) {
2026-01-16 15:16:33 +08:00
// const button = options.button;
// const isLeft = button === 1;// 左键1添加 右键3删除
// const icon = `url("${isLeft ? addIcon : removeIcon}") 16 16, default`
// this.canvas.upperCanvasEl.style.cursor = icon;
2026-01-15 13:42:33 +08:00
}
// 点选工具模式下移动事件处理
2026-01-15 17:15:05 +08:00
_pointMoveHandler(options) { }
2026-01-15 13:42:33 +08:00
// 点选工具模式下抬起事件处理
2026-01-16 15:16:33 +08:00
async _pointUpHandler(options) {
2026-01-15 13:42:33 +08:00
const button = options.button;
const isLeft = button === 1;// 左键1添加 右键3删除
this.canvas.upperCanvasEl.style.cursor = this.defaultCursor;
2026-01-15 17:15:05 +08:00
const fixedObject = this.canvasManager.getFixedLayerObject();
if (!fixedObject) return console.warn("未找到固定图层");
2026-01-16 15:16:33 +08:00
const { x, y } = options.absolutePointer;
2026-01-15 17:15:05 +08:00
const width = fixedObject.width * fixedObject.scaleX;
const height = fixedObject.height * fixedObject.scaleY;
2026-01-16 15:16:33 +08:00
const X = (x - (fixedObject.left - width / 2)) / fixedObject.scaleX;
const Y = (y - (fixedObject.top - height / 2)) / fixedObject.scaleY;
const label = isLeft ? 1 : 0;
const points = [];
const labels = [];
this.pointList.forEach((item) => {
points.push([item.x, item.y]);
labels.push(item.label);
});
points.push([X, Y]);
labels.push(label);
const url = await this.getSegAnythingImage({
image_path: this.props.clothingMinIOPath,
2026-01-15 17:15:05 +08:00
type: "point",
2026-01-16 15:16:33 +08:00
points,
labels,
// type: "box",
// box: [0,0,0,0],
});
this.pointList.push({
x: X,
y: Y,
label: label,
})
const image1 = await this.loadImageToObject(url);
const group = this.partObject;
this.removeAllChildren();
const rgba = { r: 0, g: 255, b: 0, a: 200 }
const canvas = getObjectAlphaToCanvas(image1, null, 0, rgba);
const image2 = new fabric.Image(canvas);
image2.set({
originX: fixedObject.originX,
originY: fixedObject.originY,
2026-01-15 17:15:05 +08:00
});
2026-01-16 15:16:33 +08:00
group.add(image2);
for (let i = 0; i < this.pointList.length; i++) {
const item = this.pointList[i];
const icon = await this.loadImageToObject(item.label === 1 ? addIcon : removeIcon);
icon.set({
left: item.x - group.width / 2,
top: item.y - group.height / 2,
originX: fixedObject.originX,
originY: fixedObject.originY,
})
group.add(icon);
}
this.canvas.renderAll();
2026-01-15 13:42:33 +08:00
}
// 框选工具模式下点击事件处理
_rectangleDownHandler(options) {
}
// 框选工具模式下移动事件处理
_rectangleMoveHandler(options) {
}
// 框选工具模式下抬起事件处理
_rectangleUpHandler(options) {
}
// 绘制工具模式下点击事件处理
_brushDownHandler(options) {
}
// 绘制工具模式下移动事件处理
_brushMoveHandler(options) {
}
// 绘制工具模式下抬起事件处理
_brushUpHandler(options) {
}
// 擦除工具模式下抬起事件处理
_eraseUpHandler(options) {
}
// 擦除工具模式下点击事件处理
_eraseDownHandler(options) {
}
// 擦除工具模式下移动事件处理
_eraseMoveHandler(options) {
}
2026-01-15 17:15:05 +08:00
// 获取分隔后图片
async getSegAnythingImage(obj) {
2026-01-16 15:16:33 +08:00
return new Promise((resolve, reject) => {
// const user_id = store.state.UserHabit.userDetail.userId;
const user_id = 24299;
const data = {
user_id,
...obj,
}
Https.axiosPost(Https.httpUrls.segAnything, data)
.then(response => {
if (response) {
resolve(response);
} else {
console.error("获取分隔后图片失败");
}
})
.catch(error => {
console.error(error);
});
});
2026-01-15 17:15:05 +08:00
}
2026-01-16 15:16:33 +08:00
// 删除指定ID的对象
removeObjectsById(id) {
const objects = this.canvas.getObjects().filter(obj => obj.id === id);
this.canvas.remove(...objects);
}
loadImageToObject(url) {
return new Promise((resolve, reject) => {
fabric.Image.fromURL(url, (img) => {
resolve(img);
}, { crossOrigin: "anonymous" });// 防止污染
});
}
2026-01-15 13:42:33 +08:00
2026-01-16 15:16:33 +08:00
removeAllChildren() {
this.partObject?._objects?.forEach(child => {
group.remove(child);
});
}
createPartObject() {
const fixedObject = this.canvasManager.getFixedLayerObject();
if (!fixedObject) return console.warn("未找到固定图层");
const group = new fabric.Group();
group.set({
id: this.partId,
opacity: 1,
left: fixedObject.left,
top: fixedObject.top,
width: fixedObject.width,
height: fixedObject.height,
scaleX: fixedObject.scaleX,
scaleY: fixedObject.scaleY,
originX: fixedObject.originX,
originY: fixedObject.originY,
selectable: false,
evented: false,
})
this.canvas.add(group);
this.partObject = group;
}
clearPartObject() {
this.removeObjectsById(this.partId);
2026-01-15 13:42:33 +08:00
this.partObject = null;
}
/**
* 清理资源
*/
dispose() {
this.cleanupEvents();
2026-01-16 15:16:33 +08:00
this.clearPartObject();
2026-01-15 13:42:33 +08:00
this.canvas = null;
this.commandManager = null;
this.layerManager = null;
}
2026-01-14 14:43:43 +08:00
}