From 810dd2351b235067eefc4efd7c8d209b82bd19c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=BF=97=E9=B9=8F?= <2916022834@qq.com> Date: Wed, 14 Jan 2026 14:43:43 +0800 Subject: [PATCH] 111 --- .../components/PartSelectorPanel.vue | 4 + .../CanvasEditor/components/ToolsSidebar.vue | 26 +- src/component/Canvas/CanvasEditor/index.vue | 17 +- .../CanvasEditor/managers/CanvasManager.js | 13 + .../CanvasEditor/managers/PartManager.js | 941 ++++++++++++++++++ .../CanvasEditor/managers/ToolManager.js | 34 +- 6 files changed, 1008 insertions(+), 27 deletions(-) create mode 100644 src/component/Canvas/CanvasEditor/managers/PartManager.js diff --git a/src/component/Canvas/CanvasEditor/components/PartSelectorPanel.vue b/src/component/Canvas/CanvasEditor/components/PartSelectorPanel.vue index 27632237..a7f01588 100644 --- a/src/component/Canvas/CanvasEditor/components/PartSelectorPanel.vue +++ b/src/component/Canvas/CanvasEditor/components/PartSelectorPanel.vue @@ -94,6 +94,10 @@ type: Object, required: true, }, + partManager: { + type: Object, + required: true, + }, layerManager: { type: Object, required: true, diff --git a/src/component/Canvas/CanvasEditor/components/ToolsSidebar.vue b/src/component/Canvas/CanvasEditor/components/ToolsSidebar.vue index 379fa38c..b1f29cb0 100644 --- a/src/component/Canvas/CanvasEditor/components/ToolsSidebar.vue +++ b/src/component/Canvas/CanvasEditor/components/ToolsSidebar.vue @@ -166,19 +166,19 @@ const normalToolsList = ref([ icon: { name: "CFont", size: "20" }, class: "text-btn", }, -// { -// id: OperationType.PART, -// title: t("Canvas.GarmentPartSelector"), -// action: () => selectTool(OperationType.PART), -// icon: { name: "CPart", size: "28" }, -// class: "part-btn", -// activeList: [ -// OperationType.PART, -// OperationType.PART_RECTANGLE, -// OperationType.PART_BRUSH, -// OperationType.PART_ERASER, -// ], -// }, + { + id: OperationType.PART, + title: t("Canvas.GarmentPartSelector"), + action: () => selectTool(OperationType.PART), + icon: { name: "CPart", size: "28" }, + class: "part-btn", + activeList: [ + OperationType.PART, + OperationType.PART_RECTANGLE, + OperationType.PART_BRUSH, + OperationType.PART_ERASER, + ], + }, { id: "help", title: t("Canvas.help"), diff --git a/src/component/Canvas/CanvasEditor/index.vue b/src/component/Canvas/CanvasEditor/index.vue index 58ec3023..d927f742 100644 --- a/src/component/Canvas/CanvasEditor/index.vue +++ b/src/component/Canvas/CanvasEditor/index.vue @@ -17,6 +17,7 @@ import { KeyboardManager } from "./managers/events/KeyboardManager.js"; import CanvasConfig from "./config/canvasConfig.js"; import { LiquifyManager } from "./managers/liquify/LiquifyManager"; import { SelectionManager } from "./managers/selection/SelectionManager"; +import { PartManager } from "./managers/PartManager"; import { RedGreenModeManager } from "./managers/RedGreenModeManager"; import texturePresetManager from "./managers/brushes/TexturePresetManager"; import { BrushStore } from "./store/BrushStore"; @@ -185,6 +186,7 @@ let keyboardManager = null; let toolManager = null; let liquifyManager = null; let selectionManager = null; +let partManager = null; let redGreenModeManager = null; // 快捷键帮助模态框状态 @@ -226,6 +228,7 @@ function handleCanvasInit(isLoadJson = false) { keyboardManager, liquifyManager, selectionManager, + partManager, redGreenModeManager, }); } @@ -378,6 +381,13 @@ onMounted(async () => { }); canvasManager.setSelectionManager(selectionManager); + // 初始化部件选择管理器 + partManager = new PartManager({ + canvas: canvasManager.canvas, + layerManager, + }); + canvasManager.setPartManager(partManager); + if (props.canvasJSON) { // 如果传入了初始JSON数据,加载到画布上 if (typeof props.canvasJSON === "string") { @@ -537,6 +547,7 @@ onBeforeUnmount(async () => { toolManager?.dispose?.(); liquifyManager?.dispose?.(); selectionManager?.dispose?.(); + partManager?.dispose?.(); redGreenModeManager?.dispose?.(); // minimapManager?.dispose?.(); canvasManager = null; @@ -546,6 +557,7 @@ onBeforeUnmount(async () => { toolManager = null; liquifyManager = null; selectionManager = null; + partManager = null; redGreenModeManager = null; // fabric.Object.prototype.controls.deleteControl = undefined; @@ -1254,16 +1266,17 @@ defineExpose({ /> - + /> { + // 如果选区功能未激活,不处理事件 + if (!this.isActive) return; + + // 如果点击的是已有对象且不是选区对象,则不处理 + if ( + options.target && + options.target.id !== this.selectionId && + options.target.selectable !== false && + options.target.type !== "selection" + ) { + return; + } + + // 阻止事件冒泡,避免与 CanvasEventManager 冲突 + options.e.stopPropagation(); + + // 根据选区类型执行不同的起始操作 + switch (this.selectionType) { + case OperationType.LASSO: + this.startFreeSelection(options); + break; + case OperationType.LASSO_ELLIPSE: + this.startEllipseSelection(options); + break; + case OperationType.LASSO_RECTANGLE: + this.startRectangleSelection(options); + break; + } + }; + + // 鼠标移动事件处理 + this._mouseMoveHandler = (options) => { + // 如果选区功能未激活或没有正在绘制的对象,不处理事件 + if (!this.isActive || !this.drawingObject) return; + + // 阻止事件冒泡 + options.e.stopPropagation(); + + // 根据选区类型执行不同的绘制操作 + switch (this.selectionType) { + case OperationType.LASSO_RECTANGLE: + this.drawRectangleSelection(options); + break; + case OperationType.LASSO_ELLIPSE: + this.drawEllipseSelection(options); + break; + case OperationType.LASSO: + this.drawFreeSelection(options); + break; + } + }; + + // 鼠标抬起事件处理 + this._mouseUpHandler = (options) => { + // 如果选区功能未激活或没有正在绘制的对象,不处理事件 + if (!this.isActive || !this.drawingObject) return; + + // 阻止事件冒泡 + if (options && options.e) { + options.e.stopPropagation(); + } + + // 根据选区类型执行不同的完成操作 + switch (this.selectionType) { + case OperationType.LASSO_RECTANGLE: + this.endRectangleSelection(); + break; + case OperationType.LASSO_ELLIPSE: + this.endEllipseSelection(); + break; + case OperationType.LASSO: + this.endFreeSelection(); + break; + } + + // 如果有命令管理器,使用命令模式记录选区创建 + if (this.commandManager && this.selectionObject) { + this.commandManager.execute( + new CreateSelectionCommand({ + canvas: this.canvas, + selectionManager: this, + selectionObject: this.selectionObject, + selectionType: this.selectionType, + }) + ); + } + }; + + // 键盘事件处理 + this._keyDownHandler = (event) => { + // 只在选区功能激活时处理键盘事件 + if (!this.isActive) return; + + if (event.key === "Escape") { + // ESC键取消当前选区操作 + if (this.drawingObject) { + this.canvas.remove(this.drawingObject); + this.drawingObject = null; + this.startPoint = null; + } + // 清除已有选区 + else if (this.selectionObject) { + if (this.commandManager) { + this.commandManager.execute( + new ClearSelectionCommand({ + selectionManager: this, + }) + ); + } else { + this.clearSelection(); + } + } + } + }; + + // 添加事件监听 + 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; + } + } + + /** + * 获取选区对象 + * @returns {Object} 选区对象 + */ + getSelectionObject() { + return this.selectionObject; + } + + /** + * 获取选区路径 + * @returns {Array|String} 选区路径数据 + */ + getSelectionPath() { + return this.selectionPath; + } + + /** + * 获取羽化值 + * @returns {Number} 羽化值 + */ + getFeatherAmount() { + return this.featherAmount; + } + + /** + * 设置羽化值 + * @param {Number} amount 羽化值 + */ + setFeatherAmount(amount) { + this.featherAmount = amount; + return this.updateSelectionAppearance(); + } + + /** + * 设置选区对象 + * @param {Object} object 选区对象 + */ + setSelectionObject(object) { + // 如果已存在选区,先移除 + if (this.selectionObject) { + this.removeSelectionFromCanvas(); + } + + // 更新选区对象 + this.selectionObject = object; + this.selectionPath = object.path; + this.selectionId = object.id || generateId(); + + // 更新外观 + this.updateSelectionAppearance(); + + // 添加到画布(确保在顶层) + if (this.canvas && this.selectionObject) { + this.canvas.add(this.selectionObject); + this.canvas.bringToFront(this.selectionObject); + this.canvas.renderAll(); + } + + // 触发选区变化回调 + if (this.onSelectionChanged && typeof this.onSelectionChanged === "function") { + this.onSelectionChanged(); + } + + return true; + } + + /** + * 从路径数据设置选区 + * @param {Array|String} path 选区路径数据 + */ + setSelectionFromPath(path) { + if (!path) return false; + + // 创建选区对象 + const selectionObj = new fabric.Path(path, { + ...this.selectionStyle, + id: `selection_${Date.now()}`, + name: "selection", + }); + + // 设置选区 + return this.setSelectionObject(selectionObj); + } + + /** + * 更新选区外观 + */ + updateSelectionAppearance() { + if (!this.selectionObject) return false; + + // 应用基本样式 + Object.assign(this.selectionObject, this.selectionStyle); + + // 应用羽化效果 + if (this.featherAmount > 0) { + this.selectionObject.shadow = new fabric.Shadow({ + color: "rgba(0, 150, 255, 0.5)", + blur: this.featherAmount, + offsetX: 0, + offsetY: 0, + }); + } else { + this.selectionObject.shadow = null; + } + + // 更新画布 + this.canvas.renderAll(); + return true; + } + + /** + * 移除选区 + */ + removeSelectionFromCanvas() { + if (this.canvas && this.selectionObject) { + this.canvas.remove(this.selectionObject); + this.canvas.renderAll(); + } + } + + /** + * 清除选区 + */ + clearSelection() { + // 移除选区对象 + this.removeSelectionFromCanvas(); + + // 重置选区状态 + this.selectionObject = null; + this.selectionPath = null; + this.selectionId = null; + this.featherAmount = 0; + + // 触发选区变化回调 + if (this.onSelectionChanged && typeof this.onSelectionChanged === "function") { + this.onSelectionChanged(); + } + + return true; + } + + /** + * 反转选区 + */ + async invertSelection() { + if (!this.canvas || !this.selectionObject) return false; + + // 获取画布范围 + const canvasRect = new fabric.Rect({ + left: 0, + top: 0, + width: this.canvas.width, + height: this.canvas.height, + selectable: false, + }); + + // 创建反选路径 + let invertedPath; + try { + invertedPath = canvasRect.subtractPathFromRect(this.selectionObject.path); + } catch (error) { + console.error("无法反转选区:", error); + return false; + } + + // 设置新的选区 + const newSelection = new fabric.Path(invertedPath.path, { + ...this.selectionStyle, + id: `selection_${Date.now()}`, + name: "selection", + }); + + return this.setSelectionObject(newSelection); + } + + /** + * 添加到选区 + * @param {Object} newSelection 要添加的选区对象 + */ + async addToSelection(newSelection) { + if (!this.canvas) return false; + + // 如果当前没有选区,直接使用新选区 + if (!this.selectionObject) { + return this.setSelectionObject(newSelection); + } + + // 合并选区 + let combinedPath; + try { + combinedPath = this.selectionObject.union(newSelection); + } catch (error) { + console.error("无法添加到选区:", error); + return false; + } + + // 设置新的选区 + const combinedSelection = new fabric.Path(combinedPath.path, { + ...this.selectionStyle, + id: `selection_${Date.now()}`, + name: "selection", + }); + + return this.setSelectionObject(combinedSelection); + } + + /** + * 从选区中移除 + * @param {Object} removeSelection 要移除的选区对象 + */ + async removeFromSelection(removeSelection) { + if (!this.canvas || !this.selectionObject) return false; + + // 从当前选区中减去新选区 + let resultPath; + try { + resultPath = this.selectionObject.subtract(removeSelection); + } catch (error) { + console.error("无法从选区中移除:", error); + return false; + } + + // 设置新的选区 + const newSelection = new fabric.Path(resultPath.path, { + ...this.selectionStyle, + id: `selection_${Date.now()}`, + name: "selection", + }); + + return this.setSelectionObject(newSelection); + } + + /** + * 应用羽化效果 + * @param {Number} amount 羽化值 + */ + async featherSelection(amount) { + if (!this.selectionObject) return false; + + // 更新羽化值 + this.featherAmount = amount; + + // 更新选区外观 + return this.updateSelectionAppearance(); + } + + /** + * 检查对象是否在选区内 + * @param {Object} object 要检查的对象 + * @returns {Boolean} 是否在选区内 + */ + isObjectInSelection(object) { + if (!this.selectionObject || !object) return false; + + // 获取对象的边界框 + const bounds = object.getBoundingRect(); + const { left, top, width, height } = bounds; + + // 检查对象的中心点和四个角是否在选区内 + const centerX = left + width / 2; + const centerY = top + height / 2; + + // 检查中心点 + if (this.isPointInSelection(centerX, centerY)) return true; + + // 检查四个角 + if (this.isPointInSelection(left, top)) return true; + if (this.isPointInSelection(left + width, top)) return true; + if (this.isPointInSelection(left, top + height)) return true; + if (this.isPointInSelection(left + width, top + height)) return true; + + return false; + } + + /** + * 检查点是否在选区内 + * @param {Number} x X坐标 + * @param {Number} y Y坐标 + * @returns {Boolean} 是否在选区内 + */ + isPointInSelection(x, y) { + if (!this.selectionObject) return false; + + // 使用fabric.js的containsPoint方法判断点是否在选区内 + return this.selectionObject.containsPoint({ x, y }); + } + + /** + * 开始自由选区 + * @param {Object} options 事件对象 + */ + startFreeSelection(options) { + if (!this.canvas || !this.isActive) return; + + // 获取鼠标位置 + const pointer = this.canvas.getPointer(options.e); + this.startPoint = pointer; + + // 创建用于绘制轨迹的点数组 + this.drawingPoints = [pointer]; + + // 初始化SVG路径字符串 + this.currentPathString = `M ${pointer.x} ${pointer.y}`; + + // 创建临时路径对象用于实时显示 + this.drawingObject = new fabric.Path(this.currentPathString, { + stroke: this.selectionStyle.stroke, + strokeWidth: this.selectionStyle.strokeWidth, + strokeDashArray: this.selectionStyle.strokeDashArray, + fill: "transparent", + selectable: false, + evented: false, + strokeLineCap: "round", + strokeLineJoin: "round", + }); + + // 添加到画布 + this.canvas.add(this.drawingObject); + this.canvas.renderAll(); + } + + /** + * 绘制自由选区 + * @param {Object} options 事件对象 + */ + drawFreeSelection(options) { + if (!this.drawingObject || !this.drawingPoints || !this.isActive) return; + + // 获取鼠标位置 + const pointer = this.canvas.getPointer(options.e); + + // 添加新的点,但避免添加过于密集的点 + const lastPoint = this.drawingPoints[this.drawingPoints.length - 1]; + const distance = Math.sqrt( + Math.pow(pointer.x - lastPoint.x, 2) + Math.pow(pointer.y - lastPoint.y, 2) + ); + + // 只有当距离大于2像素时才添加新点,避免路径过于复杂 + if (distance > 2) { + this.drawingPoints.push(pointer); + + // 更新路径字符串 + this.currentPathString += ` L ${pointer.x} ${pointer.y}`; + + // 移除旧的绘制对象 + this.canvas.remove(this.drawingObject); + + // 创建新的路径对象 + this.drawingObject = new fabric.Path(this.currentPathString, { + stroke: this.selectionStyle.stroke, + strokeWidth: this.selectionStyle.strokeWidth, + strokeDashArray: this.selectionStyle.strokeDashArray, + fill: "transparent", + selectable: false, + evented: false, + strokeLineCap: "round", + strokeLineJoin: "round", + }); + + // 重新添加到画布 + this.canvas.add(this.drawingObject); + this.canvas.renderAll(); + } + } + + /** + * 结束自由选区 + */ + endFreeSelection() { + if (!this.drawingObject || !this.drawingPoints || !this.isActive) return; + + // 检查是否有足够的点来形成选区 + if (this.drawingPoints.length < 3) { + // 点太少,清除绘制对象 + this.canvas.remove(this.drawingObject); + this.drawingObject = null; + this.drawingPoints = null; + this.startPoint = null; + this.currentPathString = null; + return; + } + + // 自动闭合路径 - 连接最后一点到第一点 + const firstPoint = this.drawingPoints[0]; + const lastPoint = this.drawingPoints[this.drawingPoints.length - 1]; + const closingDistance = Math.sqrt( + Math.pow(firstPoint.x - lastPoint.x, 2) + Math.pow(firstPoint.y - lastPoint.y, 2) + ); + + // 如果首尾距离较大,自动添加闭合线段 + let finalPathString = this.currentPathString; + if (closingDistance > 10) { + finalPathString += ` L ${firstPoint.x} ${firstPoint.y}`; + } + finalPathString += " Z"; // 闭合路径 + + // 创建最终选区对象 + const selectionObj = new fabric.Path(finalPathString, { + ...this.selectionStyle, + id: `selection_${Date.now()}`, + name: "selection", + fill: this.selectionStyle.fill, // 恢复填充 + }); + + // 移除绘制中的临时对象 + this.canvas.remove(this.drawingObject); + + // 重置绘制状态 + this.drawingObject = null; + this.drawingPoints = null; + this.startPoint = null; + this.currentPathString = null; + + // 设置选区 + this.setSelectionObject(selectionObj); + } + + /** + * 开始矩形选区 + * @param {Object} options 事件对象 + */ + startRectangleSelection(options) { + if (!this.canvas || !this.isActive) return; + + // 获取鼠标位置 + const pointer = this.canvas.getPointer(options.e); + this.startPoint = pointer; + + // 创建矩形对象 + this.drawingObject = new fabric.Rect({ + left: pointer.x, + top: pointer.y, + width: 0, + height: 0, + ...this.selectionStyle, + fill: "transparent", // 在绘制过程中不显示填充 + }); + + // 添加到画布 + this.canvas.add(this.drawingObject); + this.canvas.renderAll(); + } + + /** + * 绘制矩形选区 + * @param {Object} options 事件对象 + */ + drawRectangleSelection(options) { + if (!this.drawingObject || !this.startPoint || !this.isActive) return; + + // 获取鼠标位置 + const pointer = this.canvas.getPointer(options.e); + + // 计算宽度和高度 + const width = Math.abs(pointer.x - this.startPoint.x); + const height = Math.abs(pointer.y - this.startPoint.y); + + // 确定左上角坐标 + const left = Math.min(this.startPoint.x, pointer.x); + const top = Math.min(this.startPoint.y, pointer.y); + + // 更新矩形 + this.drawingObject.set({ + left: left, + top: top, + width: width, + height: height, + }); + + this.canvas.renderAll(); + } + + /** + * 结束矩形选区 + */ + endRectangleSelection() { + if (!this.drawingObject || !this.startPoint || !this.isActive) return; + + // 将矩形转换为路径 + const left = this.drawingObject.left; + const top = this.drawingObject.top; + const width = this.drawingObject.width; + const height = this.drawingObject.height; + + // 如果矩形太小,忽略 + if (width < 5 || height < 5) { + this.canvas.remove(this.drawingObject); + this.drawingObject = null; + this.startPoint = null; + return; + } + + // 创建矩形路径字符串 + const pathString = `M ${left} ${top} L ${left + width} ${top} L ${ + left + width + } ${top + height} L ${left} ${top + height} Z`; + + // 创建最终选区对象 + const selectionObj = new fabric.Path(pathString, { + ...this.selectionStyle, + id: `selection_${Date.now()}`, + name: "selection", + fill: this.selectionStyle.fill, // 恢复填充 + }); + + // 移除绘制中的临时对象 + this.canvas.remove(this.drawingObject); + + // 重置绘制状态 + this.drawingObject = null; + this.startPoint = null; + + // 设置选区 + this.setSelectionObject(selectionObj); + } + + /** + * 开始椭圆选区 + * @param {Object} options 事件对象 + */ + startEllipseSelection(options) { + if (!this.canvas || !this.isActive) return; + + // 获取鼠标位置 + const pointer = this.canvas.getPointer(options.e); + this.startPoint = pointer; + + // 创建椭圆对象 + this.drawingObject = new fabric.Ellipse({ + left: pointer.x, + top: pointer.y, + rx: 0, + ry: 0, + ...this.selectionStyle, + fill: "transparent", // 在绘制过程中不显示填充 + // originX: "left", + // originY: "top", + originX: "center", + originY: "center", + }); + + // 添加到画布 + this.canvas.add(this.drawingObject); + this.canvas.renderAll(); + } + + /** + * 绘制椭圆选区 + * @param {Object} options 事件对象 + */ + drawEllipseSelection(options) { + if (!this.drawingObject || !this.startPoint || !this.isActive) return; + + // 获取鼠标位置 + const pointer = this.canvas.getPointer(options.e); + + // 计算半径 + const rx = Math.abs(pointer.x - this.startPoint.x) / 2; + const ry = Math.abs(pointer.y - this.startPoint.y) / 2; + + // 确定中心坐标 + const left = Math.min(this.startPoint.x, pointer.x); + const top = Math.min(this.startPoint.y, pointer.y); + + // 更新椭圆 + this.drawingObject.set({ + left: left, + top: top, + rx: rx, + ry: ry, + originX: "left", + originY: "top", + }); + + this.canvas.renderAll(); + } + + /** + * 结束椭圆选区 + */ + endEllipseSelection() { + if (!this.drawingObject || !this.startPoint || !this.isActive) return; + + // 获取椭圆参数 + const { left, top, rx, ry } = this.drawingObject; + + // 如果椭圆太小,忽略 + if (rx < 2 || ry < 2) { + this.canvas.remove(this.drawingObject); + this.drawingObject = null; + this.startPoint = null; + return; + } + + // 计算中心点 + const cx = left + rx; + const cy = top + ry; + + // 将椭圆转换为路径字符串 + const pathString = this.ellipseToSVGPath(cx, cy, rx, ry); + + // 创建最终选区对象 + const selectionObj = new fabric.Path(pathString, { + ...this.selectionStyle, + id: `selection_${Date.now()}`, + name: "selection", + fill: this.selectionStyle.fill, // 恢复填充 + }); + + // 移除绘制中的临时对象 + this.canvas.remove(this.drawingObject); + + // 重置绘制状态 + this.drawingObject = null; + this.startPoint = null; + + // 设置选区 + this.setSelectionObject(selectionObj); + } + + /** + * 将椭圆转换为SVG路径字符串 + * @param {Number} cx 中心点X坐标 + * @param {Number} cy 中心点Y坐标 + * @param {Number} rx X半径 + * @param {Number} ry Y半径 + * @returns {String} SVG路径字符串 + */ + ellipseToSVGPath(cx, cy, rx, ry) { + // 使用椭圆弧命令创建完整椭圆 + return `M ${cx - rx} ${cy} A ${rx} ${ry} 0 1 0 ${ + cx + rx + } ${cy} A ${rx} ${ry} 0 1 0 ${cx - rx} ${cy} Z`; + } + + /** + * 设置选区工具 + * @param {string} type 选区类型:OperationType.LASSO, OperationType.LASSO_RECTANGLE, OperationType.LASSO_ELLIPSE + */ + setSelectionType(type) { + this.selectionType = type; + + // 如果正在绘制,清除临时对象 + if (this.drawingObject) { + this.canvas.remove(this.drawingObject); + this.drawingObject = null; + this.startPoint = null; + } + } + + /** + * 设置选区工具的鼠标事件 + */ + setupSelectionEvents() { + // 选区事件现在通过 setCurrentTool 方法管理 + // 这个方法现在主要用于刷新或重置事件监听 + if (!this.canvas || !this.isActive) return; + + // 确保选区处于激活状态 + if (this.tools.includes(this.currentTool)) { + this.isActive = true; + // 如果事件还没有初始化,初始化它们 + if (!this._mouseDownHandler) { + this.initEvents(); + } + } + } + + /** + * 清理资源 + */ + dispose() { + this.cleanupEvents(); + this.clearSelection(); + this.canvas = null; + this.commandManager = null; + this.layerManager = null; + } +} diff --git a/src/component/Canvas/CanvasEditor/managers/ToolManager.js b/src/component/Canvas/CanvasEditor/managers/ToolManager.js index 7c0427b6..db048368 100644 --- a/src/component/Canvas/CanvasEditor/managers/ToolManager.js +++ b/src/component/Canvas/CanvasEditor/managers/ToolManager.js @@ -190,30 +190,26 @@ export class ToolManager { [OperationType.PART]: { name: "部件选取工具", icon: "part", - cursor: "crosshair", - // setup: this.setupLassoTool.bind(this), - specialLayerDisabled: true, + cursor: "default", + setup: this.setupPartTool.bind(this), }, [OperationType.PART_RECTANGLE]: { name: "部件选取工具-矩形", icon: "part", - cursor: "crosshair", - // setup: this.setupRectangleLassoTool.bind(this), - specialLayerDisabled: true, + cursor: "default", + setup: this.setupPartTool.bind(this), }, [OperationType.PART_BRUSH]: { name: "部件选取工具-画笔", icon: "part", - cursor: "crosshair", - // setup: this.setupEllipseLassoTool.bind(this), - specialLayerDisabled: true, + cursor: "default", + setup: this.setupPartTool.bind(this), }, [OperationType.PART_ERASER]: { name: "部件选取工具-橡皮擦", icon: "part", - cursor: "crosshair", - // setup: this.setupEllipseLassoTool.bind(this), - specialLayerDisabled: true, + cursor: "default", + setup: this.setupPartTool.bind(this), }, // 红绿图模式专用工具 @@ -704,6 +700,20 @@ export class ToolManager { } } + /** + * 设置部件选取工具 + */ + setupPartTool() { + if (!this.canvas) return; + if (this.checkToolCanOperateSelectedObject()) return; + this.canvas.isDrawingMode = false; + this.canvas.selection = false; + + if (this.canvasManager && this.canvasManager.partManager) { + this.canvasManager.partManager.setCurrentTool(OperationType.PART); + } + } + /** * 设置波浪工具 */