import { fabric } from 'fabric-with-all' import { OperationType } from '../tools/layerHelper' import { getObjectAlphaToCanvas, traceImageContour, cloneObjects } from '../tools/canvasMethod' /** 智能框选工具管理器 */ export class AISelectboxToolManager { // 管理器 canvasManager: any stateManager: any layerManager: any toolManager: any isDragging: boolean = false startX: number = 0 startY: number = 0 indicatorObject: any// 指示框对象 demoObject: any// 演示框对象 tcanvas: any// 临时画布对象 tools = [ OperationType.AISELECT_ADD, OperationType.AISELECT_REMOVE, OperationType.AISELECT_DRAW, OperationType.AISELECT_ERASER ] constructor(options) { this.canvasManager = options.canvasManager this.stateManager = options.stateManager this.layerManager = options.layerManager this.toolManager = options.toolManager } /** 处理切换工具 */ handleToolChange(oldTool: string, newTool: string) { const oldIsAAA = this.tools.includes(oldTool) const newIsAAA = this.tools.includes(newTool) if (!oldIsAAA && newIsAAA) { // 普通工具切换到智能框选工具 this.init() } else if (oldIsAAA && !newIsAAA) { // 智能框选工具切换到普通工具 this.clear() } } /** 切换到橡皮擦工具 */ changeToolToEraser() { if (!this.demoObject) return; this.demoObject.set({ erasable: true }) } init() { console.log("初始化智能框选工具") this.clear(); this.createDemoObject() this.tcanvas = null; } clear() { console.log("清除智能框选工具") this.clearDemoObject() this.clearIndicatorObject() this.isDragging = false this.canvasManager.canvas.renderAll() } createDemoObject() { if (this.demoObject) this.clearDemoObject() const { canvasWidth, canvasHeight } = this.canvasManager.getCanvasSize(); const canvas = document.createElement('canvas') canvas.width = canvasWidth canvas.height = canvasHeight this.demoObject = new fabric.Image(canvas, { left: 0, top: 0, evented: false, selectable: false, erasable: false, }) this.canvasManager.canvas.add(this.demoObject) this.canvasManager.canvas.renderAll() } clearDemoObject() { this.canvasManager.canvas.remove(this.demoObject) this.demoObject = null } createIndicatorObject() { const rect = new fabric.Rect({ left: this.startX, top: this.startY, width: 0, height: 0, fill: 'transparent', stroke: '#000', strokeWidth: 1, evented: false, }) this.indicatorObject = rect this.canvasManager.canvas.add(rect) this.canvasManager.canvas.renderAll() } clearIndicatorObject() { this.canvasManager.canvas.remove(this.indicatorObject) this.indicatorObject = null } // 处理画笔绘制图像 async handleBrushDrawImage(fabricImage: fabric.Object) { if (!this.demoObject) return; const tcanvas = new fabric.StaticCanvas(document.createElement("canvas"), { width: this.demoObject.width, height: this.demoObject.height, enableRetinaScaling: false, }); const demoObject = await cloneObjects([this.demoObject]).then(v => v[0]) tcanvas.add(demoObject) tcanvas.add(fabricImage) tcanvas.renderAll(); const canvas = getObjectAlphaToCanvas(tcanvas, null, 0, this.rgba); const image = new fabric.Image(canvas); this.canvasManager.canvas.add(image) this.canvasManager.canvas.remove(this.demoObject); this.demoObject = image; this.canvasManager.canvas.renderAll() } mouseDownEvent(e) { // if (true) return const tool = this.toolManager.currentTool.value const tools = [OperationType.AISELECT_ADD, OperationType.AISELECT_REMOVE] if (!tools.includes(tool)) return; this.isDragging = true this.startX = e.absolutePointer.x this.startY = e.absolutePointer.y this.createIndicatorObject() } mouseMoveEvent(e) { if (!this.isDragging) return; var width = e.absolutePointer.x - this.startX var height = e.absolutePointer.y - this.startY var left = this.startX var top = this.startY if (width < 0) { left += width width = -width } if (height < 0) { top += height height = -height } this.indicatorObject.set({ width, height, left, top }) this.canvasManager.canvas.renderAll() } mouseUpEvent(e) { if (!this.isDragging) return; this.isDragging = false; const object = this.indicatorObject.toJSON("evented") if (object.width === 0) object.width = 100 if (object.height === 0) object.height = 100 console.log(object.top, object.left, object.width, object.height) this.clearIndicatorObject() this.canvasManager.canvas.renderAll() // this.createSelectbox() } loadImageToObject(url) { return new Promise((resolve, reject) => { fabric.Image.fromURL(url, (img) => { resolve(img); }, { crossOrigin: "anonymous" });// 防止污染 }); } rgba = { r: 255, g: 0, b: 0, a: 127.5 }; selectionStyle = { stroke: "rgba(255, 77, 71, 1)", strokeWidth: 1.5, strokeDashArray: [4, 4], fill: "transparent", strokeUniform: true, // 保持描边宽度不随缩放改变 selectable: false, evented: false, absolutePositioned: true, }; async createSelectbox() { // const url = "http://118.31.39.42:3000/falls/1a48ed3a-1faa-4fcd-bf07-765dba1702c5.png" // const image = await this.loadImageToObject(url) // const fobject = this.canvasManager.canvas.clipPath const fobject = this.demoObject this.clearDemoObject() const canvas = getObjectAlphaToCanvas(fobject, null, 0, { r: 255, g: 0, b: 0, a: 255 }); const scaleY = fobject.scaleY const scaleX = fobject.scaleX const top = fobject.top const left = fobject.left const arr = traceImageContour(canvas); let minX = fobject.width; let minY = fobject.height; const str = arr.map((v) => { if (v.x < minX) minX = v.x; if (v.y < minY) minY = v.y; return `${v.x} ${v.y}` }).join(" L "); const path = new fabric.Path(`M ${str} z`); path.set({ left: left + minX, top: top + minY, scaleX: scaleX, scaleY: scaleY, ...this.selectionStyle, }); const group = await this.layerManager.createGroupLayer({ clipPath: path, top: path.top, left: path.left, }, false, false) const rect = await this.layerManager.createRectLayer({ width: path.width, height: path.height, left: left + minX, top: top + minY, fill: "rgba(255, 186, 186, 0.5)", info: { parentId: group.info.id }, }, false, true) await this.canvasManager.updateSubLayerClipPath() await this.layerManager.updateLayerThumbnailsById(rect.info.id, "", false) await this.layerManager.updateLayerThumbnailsById(group.info.id, rect.thumbnail) this.stateManager.recordState() this.toolManager.setTool(OperationType.SELECT) } dispose() { this.clear() } }