fabricTool.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import { Point, Canvas } from 'fabric'
  2. import { watch, computed } from 'vue'
  3. import { storeToRefs } from 'pinia'
  4. import { Disposable } from '@/utils/lifecycle'
  5. import useCanvasSwipe from '@/hooks/useCanvasSwipe'
  6. import { useKeyboardStore } from '@/store'
  7. import { useActiveElement, toValue } from '@vueuse/core'
  8. type ToolOption = {
  9. defaultCursor: string
  10. skipTargetFind: boolean
  11. selection: boolean
  12. }
  13. type ToolType = 'move' | 'handMove' | 'shape'
  14. export class FabricTool extends Disposable {
  15. private options: Record<ToolType, ToolOption> = {
  16. move: {
  17. defaultCursor: 'default',
  18. skipTargetFind: false,
  19. selection: true,
  20. },
  21. handMove: {
  22. defaultCursor: 'grab',
  23. skipTargetFind: true,
  24. selection: false,
  25. },
  26. shape: {
  27. defaultCursor: 'crosshair',
  28. skipTargetFind: true,
  29. selection: false,
  30. },
  31. }
  32. private _handMoveActivate = false
  33. private get handMoveActivate() {
  34. return this._handMoveActivate
  35. }
  36. private set handMoveActivate(value) {
  37. this._handMoveActivate = value
  38. }
  39. constructor(private readonly canvas: Canvas) {
  40. super()
  41. this.initHandMove()
  42. }
  43. private applyOption(tool: ToolType) {
  44. const { defaultCursor, skipTargetFind, selection } = this.options[tool]
  45. this.canvas.defaultCursor = defaultCursor
  46. this.canvas.setCursor(defaultCursor)
  47. this.canvas.skipTargetFind = skipTargetFind
  48. this.canvas.selection = selection
  49. }
  50. // private switchShape(shape: 'board' | 'rect' | 'ellipse' | 'triangle' | 'text') {
  51. // const { canvas } = this
  52. // let coordsStart: Point | undefined
  53. // let tempObject: FabricObject | undefined
  54. // const { stop, isSwiping } = useFabricSwipe({
  55. // onSwipeStart: (e) => {
  56. // if (e.button !== 1 || this.space.value) return
  57. // /*
  58. // * 只有mouseMove的时候isSwiping才会为true
  59. // * mouseUp会判断isSwiping的值来决定是否执行onSwipeEnd
  60. // * 这里强制设置成true,让点击也可执行onSwipeEnd
  61. // */
  62. // isSwiping.value = true
  63. // // 获得坐标
  64. // coordsStart = e.pointer
  65. // // 创建形状
  66. // switch (shape) {
  67. // case 'board':
  68. // tempObject = new Board([], {
  69. // fill: '',
  70. // })
  71. // break
  72. // case 'rect':
  73. // tempObject = new Rect({})
  74. // break
  75. // case 'ellipse':
  76. // tempObject = new Ellipse({
  77. // rx: 50,
  78. // ry: 50,
  79. // })
  80. // break
  81. // case 'triangle':
  82. // tempObject = new Triangle({})
  83. // break
  84. // case 'text':
  85. // tempObject = new Textbox('', {})
  86. // break
  87. // }
  88. // tempObject.set({
  89. // left: coordsStart.x,
  90. // top: coordsStart.y,
  91. // width: 100,
  92. // height: 100,
  93. // scaleX: 0.01,
  94. // scaleY: 0.01,
  95. // hideOnLayer: true,
  96. // })
  97. // // 不发送ObjectAdded事件
  98. // tempObject.noEventObjectAdded = true
  99. // // 添加对象到画板
  100. // const board = this.canvas._searchPossibleTargets(
  101. // this.canvas.getObjects('Board'),
  102. // e.absolutePointer,
  103. // ) as Board | undefined
  104. // const parent = board || canvas
  105. // parent.add(tempObject)
  106. // // 取消不发送
  107. // tempObject.noEventObjectAdded = false
  108. // // 设置激活对象
  109. // canvas.setActiveObject(tempObject)
  110. // tempObject.__corner = 'br'
  111. // canvas._setupCurrentTransform(e.e, tempObject, true)
  112. // },
  113. // onSwipeEnd: (e) => {
  114. // if (!tempObject) return
  115. // console.log('onSwipeEnd:', tempObject)
  116. // // 如果点击画板,没有移动,设置默认宽高
  117. // if (tempObject.scaleX <= 0.01 && tempObject.scaleY <= 0.01) {
  118. // tempObject.set({
  119. // left: tempObject.left - 50,
  120. // top: tempObject.top - 50,
  121. // scaleX: 1,
  122. // scaleY: 1,
  123. // })
  124. // }
  125. // // 设置宽高缩放
  126. // tempObject.set({
  127. // width: tempObject.getScaledWidth(),
  128. // height: tempObject.getScaledHeight(),
  129. // scaleX: 1,
  130. // scaleY: 1,
  131. // hideOnLayer: false,
  132. // })
  133. // // 特殊形状处理
  134. // if (tempObject instanceof Board) {
  135. // tempObject.set({
  136. // fill: '#ffffff',
  137. // })
  138. // } else if (tempObject instanceof Ellipse) {
  139. // tempObject.set({
  140. // rx: tempObject.width / 2,
  141. // ry: tempObject.height / 2,
  142. // })
  143. // } else if (tempObject instanceof Textbox) {
  144. // tempObject.set({
  145. // text: '输入文本',
  146. // })
  147. // canvas.defaultCursor = 'default'
  148. // tempObject.enterEditing(e.e)
  149. // tempObject.selectAll()
  150. // }
  151. // // 通知事件
  152. // if (!tempObject.group) {
  153. // canvas._onObjectAdded(tempObject)
  154. // }
  155. // canvas.fire('selection:updated')
  156. // canvas.requestRenderAll()
  157. // tempObject = undefined
  158. // useAppStore().activeTool = 'move'
  159. // },
  160. // })
  161. // this.toolStop = stop
  162. // }
  163. /**
  164. *鼠标中键拖动视窗
  165. */
  166. private initHandMove() {
  167. const canvas = this.canvas
  168. /** 鼠标移动开始的vpt */
  169. let vpt = canvas.viewportTransform
  170. const { spaceKeyState } = storeToRefs(useKeyboardStore())
  171. const { lengthX, lengthY, isSwiping } = useCanvasSwipe({
  172. onSwipeStart: (e: any) => {
  173. if (e.e.buttons === 2 || (spaceKeyState.value && e.e.buttons === 1)) {
  174. isSwiping.value = true
  175. vpt = canvas.viewportTransform
  176. this.handMoveActivate = true
  177. this.applyOption('handMove')
  178. canvas.setCursor('grab')
  179. }
  180. },
  181. onSwipe: () => {
  182. if (!this.handMoveActivate) return
  183. canvas.setCursor('grab')
  184. requestAnimationFrame(() => {
  185. const deltaPoint = new Point(lengthX.value, lengthY.value).scalarDivide(canvas.getZoom()).transform(vpt).scalarMultiply(-1)
  186. canvas.absolutePan(deltaPoint, true)
  187. })
  188. },
  189. onSwipeEnd: () => {
  190. // 恢复鼠标指针
  191. this.applyOption(spaceKeyState.value ? 'handMove' : 'move')
  192. if (!this.handMoveActivate) return
  193. // 关闭 handMove
  194. if (!spaceKeyState.value) {
  195. this.handMoveActivate = false
  196. }
  197. },
  198. })
  199. // 空格键切换移动工具
  200. const activeElement = useActiveElement()
  201. const activeElementHasInput = computed(() => activeElement.value?.tagName !== 'INPUT' && activeElement.value?.tagName !== 'TEXTAREA')
  202. watch(
  203. computed(() => [spaceKeyState.value, activeElementHasInput.value].every((i) => toValue(i))),
  204. (space) => {
  205. this.applyOption(space ? 'handMove' : 'move')
  206. if (isSwiping.value) return
  207. this.handMoveActivate = space
  208. },
  209. )
  210. }
  211. }