import { Point, Canvas } from 'fabric' import { watch, computed } from 'vue' import { storeToRefs } from 'pinia' import { Disposable } from '@/utils/lifecycle' import useCanvasSwipe from '@/hooks/useCanvasSwipe' import { useKeyboardStore } from '@/store' import { useActiveElement, toValue } from '@vueuse/core' type ToolOption = { defaultCursor: string skipTargetFind: boolean selection: boolean } type ToolType = 'move' | 'handMove' | 'shape' export class FabricTool extends Disposable { private options: Record = { move: { defaultCursor: 'default', skipTargetFind: false, selection: true, }, handMove: { defaultCursor: 'grab', skipTargetFind: true, selection: false, }, shape: { defaultCursor: 'crosshair', skipTargetFind: true, selection: false, }, } private _handMoveActivate = false private get handMoveActivate() { return this._handMoveActivate } private set handMoveActivate(value) { this._handMoveActivate = value } constructor(private readonly canvas: Canvas) { super() this.initHandMove() } private applyOption(tool: ToolType) { const { defaultCursor, skipTargetFind, selection } = this.options[tool] this.canvas.defaultCursor = defaultCursor this.canvas.setCursor(defaultCursor) this.canvas.skipTargetFind = skipTargetFind this.canvas.selection = selection } // private switchShape(shape: 'board' | 'rect' | 'ellipse' | 'triangle' | 'text') { // const { canvas } = this // let coordsStart: Point | undefined // let tempObject: FabricObject | undefined // const { stop, isSwiping } = useFabricSwipe({ // onSwipeStart: (e) => { // if (e.button !== 1 || this.space.value) return // /* // * 只有mouseMove的时候isSwiping才会为true // * mouseUp会判断isSwiping的值来决定是否执行onSwipeEnd // * 这里强制设置成true,让点击也可执行onSwipeEnd // */ // isSwiping.value = true // // 获得坐标 // coordsStart = e.pointer // // 创建形状 // switch (shape) { // case 'board': // tempObject = new Board([], { // fill: '', // }) // break // case 'rect': // tempObject = new Rect({}) // break // case 'ellipse': // tempObject = new Ellipse({ // rx: 50, // ry: 50, // }) // break // case 'triangle': // tempObject = new Triangle({}) // break // case 'text': // tempObject = new Textbox('', {}) // break // } // tempObject.set({ // left: coordsStart.x, // top: coordsStart.y, // width: 100, // height: 100, // scaleX: 0.01, // scaleY: 0.01, // hideOnLayer: true, // }) // // 不发送ObjectAdded事件 // tempObject.noEventObjectAdded = true // // 添加对象到画板 // const board = this.canvas._searchPossibleTargets( // this.canvas.getObjects('Board'), // e.absolutePointer, // ) as Board | undefined // const parent = board || canvas // parent.add(tempObject) // // 取消不发送 // tempObject.noEventObjectAdded = false // // 设置激活对象 // canvas.setActiveObject(tempObject) // tempObject.__corner = 'br' // canvas._setupCurrentTransform(e.e, tempObject, true) // }, // onSwipeEnd: (e) => { // if (!tempObject) return // console.log('onSwipeEnd:', tempObject) // // 如果点击画板,没有移动,设置默认宽高 // if (tempObject.scaleX <= 0.01 && tempObject.scaleY <= 0.01) { // tempObject.set({ // left: tempObject.left - 50, // top: tempObject.top - 50, // scaleX: 1, // scaleY: 1, // }) // } // // 设置宽高缩放 // tempObject.set({ // width: tempObject.getScaledWidth(), // height: tempObject.getScaledHeight(), // scaleX: 1, // scaleY: 1, // hideOnLayer: false, // }) // // 特殊形状处理 // if (tempObject instanceof Board) { // tempObject.set({ // fill: '#ffffff', // }) // } else if (tempObject instanceof Ellipse) { // tempObject.set({ // rx: tempObject.width / 2, // ry: tempObject.height / 2, // }) // } else if (tempObject instanceof Textbox) { // tempObject.set({ // text: '输入文本', // }) // canvas.defaultCursor = 'default' // tempObject.enterEditing(e.e) // tempObject.selectAll() // } // // 通知事件 // if (!tempObject.group) { // canvas._onObjectAdded(tempObject) // } // canvas.fire('selection:updated') // canvas.requestRenderAll() // tempObject = undefined // useAppStore().activeTool = 'move' // }, // }) // this.toolStop = stop // } /** *鼠标中键拖动视窗 */ private initHandMove() { const canvas = this.canvas /** 鼠标移动开始的vpt */ let vpt = canvas.viewportTransform const { spaceKeyState } = storeToRefs(useKeyboardStore()) const { lengthX, lengthY, isSwiping } = useCanvasSwipe({ onSwipeStart: (e: any) => { if (e.e.buttons === 2 || (spaceKeyState.value && e.e.buttons === 1)) { isSwiping.value = true vpt = canvas.viewportTransform this.handMoveActivate = true this.applyOption('handMove') canvas.setCursor('grab') } }, onSwipe: () => { if (!this.handMoveActivate) return canvas.setCursor('grab') requestAnimationFrame(() => { const deltaPoint = new Point(lengthX.value, lengthY.value).scalarDivide(canvas.getZoom()).transform(vpt).scalarMultiply(-1) canvas.absolutePan(deltaPoint, true) }) }, onSwipeEnd: () => { // 恢复鼠标指针 this.applyOption(spaceKeyState.value ? 'handMove' : 'move') if (!this.handMoveActivate) return // 关闭 handMove if (!spaceKeyState.value) { this.handMoveActivate = false } }, }) // 空格键切换移动工具 const activeElement = useActiveElement() const activeElementHasInput = computed(() => activeElement.value?.tagName !== 'INPUT' && activeElement.value?.tagName !== 'TEXTAREA') watch( computed(() => [spaceKeyState.value, activeElementHasInput.value].every((i) => toValue(i))), (space) => { this.applyOption(space ? 'handMove' : 'move') if (isSwiping.value) return this.handMoveActivate = space }, ) } }