From 618b9bab1ff5f8014fca9795f6c1051a5a359933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=BF=97=E9=B9=8F?= <2916022834@qq.com> Date: Fri, 16 Jan 2026 15:16:33 +0800 Subject: [PATCH] fix --- .../CanvasEditor/managers/PartManager.js | 165 +++++++++++++----- .../Canvas/CanvasEditor/utils/objectHelper.js | 11 +- src/component/Canvas/canvasExample.vue | 4 +- 3 files changed, 128 insertions(+), 52 deletions(-) diff --git a/src/component/Canvas/CanvasEditor/managers/PartManager.js b/src/component/Canvas/CanvasEditor/managers/PartManager.js index c13e25b0..aa193b8c 100644 --- a/src/component/Canvas/CanvasEditor/managers/PartManager.js +++ b/src/component/Canvas/CanvasEditor/managers/PartManager.js @@ -7,6 +7,9 @@ import addIcon from "@/assets/images/canvas/add.png"; import removeIcon from "@/assets/images/canvas/remove.png"; import { Https } from "@/tool/https"; import store from "@/store"; +import { createStaticCanvas } from "../utils/canvasFactory"; +import { getObjectAlphaToCanvas } from "../utils/objectHelper"; + /** * 部件选择管理器 @@ -55,11 +58,10 @@ export class PartManager { OperationType.PART_ERASER, ]; + this.pointList = []; // 存储点选坐标 + // 当前工具 this.activeTool = this.toolManager.activeTool; - - // 选区状态变化回调 - this.onSelectionChanged = null; } /** @@ -74,11 +76,12 @@ export class PartManager { // 如果从非选区工具切换到选区工具,初始化事件 if (!wasActive && this.isActive) { this.initEvents(); + this.createPartObject(); } // 如果从选区工具切换到非选区工具,清理事件和选区 else if (wasActive && !this.isActive) { this.cleanupEvents(); - this.clearSelection(); + this.clearPartObject(); } } @@ -211,31 +214,70 @@ export class PartManager { // 点选工具模式下点击事件处理 _pointDownkHandler(options) { - 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; + // 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; } // 点选工具模式下移动事件处理 _pointMoveHandler(options) { } // 点选工具模式下抬起事件处理 - _pointUpHandler(options) { + async _pointUpHandler(options) { const button = options.button; const isLeft = button === 1;// 左键1(添加) 右键3(删除) this.canvas.upperCanvasEl.style.cursor = this.defaultCursor; const fixedObject = this.canvasManager.getFixedLayerObject(); if (!fixedObject) return console.warn("未找到固定图层"); - const { x, y } = options.pointer; + const { x, y } = options.absolutePointer; const width = fixedObject.width * fixedObject.scaleX; const height = fixedObject.height * fixedObject.scaleY; - const X = x - (fixedObject.left - width / 2); - const Y = y - (fixedObject.top - height / 2); - this.getSegAnythingImage({ - image_path: "aida-users/24299/sketch/70bb39cc-63e0-44a9-a627-3542d0f9cd70.png", - type: "point", - points: [[X, Y], [X - 10, Y - 10]], - labels: [1, 0], + 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, + type: "point", + 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, + }); + 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(); } @@ -278,38 +320,69 @@ export class PartManager { // 获取分隔后图片 async getSegAnythingImage(obj) { - const user_id = store.state.UserHabit.userDetail.userId; - const data = { - user_id, - ...obj, - } - Https.axiosPost(Https.httpUrls.segAnything, data) - .then(response => { - console.log(response); - }) - .catch(error => { - console.error(error); - }); - + 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); + }); + }); } - /** - * 清除选区 - */ - clearSelection() { - // 移除选区对象 - // this.removeSelectionFromCanvas(); + // 删除指定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" });// 防止污染 + }); + } - // 重置选区状态 + 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); this.partObject = null; - this.partPath = null; - - // 触发选区变化回调 - if (this.onSelectionChanged && typeof this.onSelectionChanged === "function") { - this.onSelectionChanged(); - } - - return true; } /** @@ -317,7 +390,7 @@ export class PartManager { */ dispose() { this.cleanupEvents(); - this.clearSelection(); + this.clearPartObject(); this.canvas = null; this.commandManager = null; this.layerManager = null; diff --git a/src/component/Canvas/CanvasEditor/utils/objectHelper.js b/src/component/Canvas/CanvasEditor/utils/objectHelper.js index 7042de27..8cf3b7ed 100644 --- a/src/component/Canvas/CanvasEditor/utils/objectHelper.js +++ b/src/component/Canvas/CanvasEditor/utils/objectHelper.js @@ -64,9 +64,10 @@ export async function restoreFabricObject(serializedObject, canvas) { * @param {fabric.Object} object - 要处理的 fabric 对象 * @param {ImageData} revData - 相反的ImageData,白通道的相同位置是否为透明,revData为白色为透明,黑色为不透明 * @param {number} diff - 差值,默认 25 + * @param {Object} rgba - 自定义 rgba 值,默认 { r: 255, g: 255, b: 255, a: 255 } * @returns {HTMLCanvasElement|null} 包含黑白通道的画布,或 null 如果失败 */ -export function getObjectAlphaToCanvas(object, revData, diff = 30) { +export function getObjectAlphaToCanvas(object, revData, diff = 30, rgba = { r: 255, g: 255, b: 255, a: 255 }) { const image = object.getElement(); const { width, height } = image; if (!width || !height) { @@ -95,10 +96,10 @@ export function getObjectAlphaToCanvas(object, revData, diff = 30) { data.data[i + 2] = 0; data.data[i + 3] = 0; } else { - data.data[i + 0] = 255; - data.data[i + 1] = 255; - data.data[i + 2] = 255; - data.data[i + 3] = 255; + data.data[i + 0] = rgba.r; + data.data[i + 1] = rgba.g; + data.data[i + 2] = rgba.b; + data.data[i + 3] = rgba.a; } } else { data.data[i + 0] = 0; diff --git a/src/component/Canvas/canvasExample.vue b/src/component/Canvas/canvasExample.vue index 945ae303..cef06618 100644 --- a/src/component/Canvas/canvasExample.vue +++ b/src/component/Canvas/canvasExample.vue @@ -8,7 +8,8 @@ // 当前显示的组件 const canvasEditor = ref(); const currentView = ref("canvasEditor"); // 默认显示红绿图示例 canvasEditor redGreenExample - + const clothingMinIOPath = + "aida-users/24299/sketchboard/female/Outwear/32d2485c-90fa-43d0-bc31-e59aaea466ba.png"; const clothingImageUrl = "/src/assets/images/canvas/xiangao.png"; const clothingImageUrlInit = "/src/assets/images/canvas/xiangaofenge.png"; @@ -404,6 +405,7 @@ ref="canvasEditor" key="canvasEditor" v-if="currentView === 'canvasEditor'" + :clothingMinIOPath="clothingMinIOPath" :clothingImageUrl="clothingImageUrl" :clothingImageUrl2="clothingImageUrlInit" :otherData="otherData"