ElementCreateSelection.vue 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. <template>
  2. <div
  3. class="element-create-selection"
  4. ref="selectionRef"
  5. @mousedown.stop="$event => createSelection($event)"
  6. >
  7. <div :class="['selection', creatingElement.type]" v-if="start && end" :style="position">
  8. <!-- 绘制线条专用 -->
  9. <SvgWrapper
  10. v-if="creatingElement.type === 'line' && lineData"
  11. overflow="visible"
  12. :width="lineData.svgWidth"
  13. :height="lineData.svgHeight"
  14. >
  15. <path
  16. :d="lineData.path"
  17. stroke="#888"
  18. fill="none"
  19. stroke-width="1"
  20. stroke-linecap
  21. stroke-linejoin
  22. stroke-miterlimit
  23. ></path>
  24. </SvgWrapper>
  25. </div>
  26. </div>
  27. </template>
  28. <script lang="ts">
  29. import { computed, defineComponent, onMounted, reactive, ref } from 'vue'
  30. import { MutationTypes, useStore } from '@/store'
  31. export default defineComponent({
  32. name: 'element-create-selection',
  33. setup(props, { emit }) {
  34. const store = useStore()
  35. const ctrlOrShiftKeyActive = computed<boolean>(() => store.getters.ctrlOrShiftKeyActive)
  36. const creatingElement = computed(() => store.state.creatingElement)
  37. const start = ref<[number, number] | null>(null)
  38. const end = ref<[number, number] | null>(null)
  39. const selectionRef = ref<HTMLElement>()
  40. const offset = reactive({
  41. x: 0,
  42. y: 0,
  43. })
  44. onMounted(() => {
  45. if(!selectionRef.value) return
  46. const { x, y } = selectionRef.value.getBoundingClientRect()
  47. offset.x = x
  48. offset.y = y
  49. })
  50. const createSelection = (e: MouseEvent) => {
  51. let isMouseDown = true
  52. const startPageX = e.pageX
  53. const startPageY = e.pageY
  54. start.value = [startPageX, startPageY]
  55. document.onmousemove = e => {
  56. if(!creatingElement.value || !isMouseDown) return
  57. let currentPageX = e.pageX
  58. let currentPageY = e.pageY
  59. if(ctrlOrShiftKeyActive.value) {
  60. const moveX = currentPageX - startPageX
  61. const moveY = currentPageY - startPageY
  62. const absX = Math.abs(moveX)
  63. const absY = Math.abs(moveY)
  64. if(creatingElement.value.type === 'shape') {
  65. // moveX和moveY一正一负
  66. const isOpposite = (moveY > 0 && moveX < 0) || (moveY < 0 && moveX > 0)
  67. if(absX > absY) {
  68. currentPageY = isOpposite ? startPageY - moveX : startPageY + moveX
  69. }
  70. else {
  71. currentPageX = isOpposite ? startPageX - moveY : startPageX + moveY
  72. }
  73. }
  74. else if(creatingElement.value.type === 'line') {
  75. if(absX > absY) currentPageY = startPageY
  76. else currentPageX = startPageX
  77. }
  78. }
  79. end.value = [currentPageX, currentPageY]
  80. }
  81. document.onmouseup = e => {
  82. document.onmousemove = null
  83. document.onmouseup = null
  84. isMouseDown = false
  85. const endPageX = e.pageX
  86. const endPageY = e.pageY
  87. const minSize = 30
  88. if(Math.abs(endPageX - startPageX) >= minSize || Math.abs(endPageY - startPageY) >= minSize) {
  89. emit('created', {
  90. start: start.value,
  91. end: end.value,
  92. })
  93. }
  94. else store.commit(MutationTypes.SET_CREATING_ELEMENT, null)
  95. }
  96. }
  97. const lineData = computed(() => {
  98. if(!start.value || !end.value) return null
  99. if(!creatingElement.value || creatingElement.value.type !== 'line') return null
  100. const [_startX, _startY] = start.value
  101. const [_endX, _endY] = end.value
  102. const minX = Math.min(_startX, _endX)
  103. const maxX = Math.max(_startX, _endX)
  104. const minY = Math.min(_startY, _endY)
  105. const maxY = Math.max(_startY, _endY)
  106. const svgWidth = maxX - minX >= 24 ? maxX - minX : 24
  107. const svgHeight = maxY - minY >= 24 ? maxY - minY : 24
  108. const startX = _startX === minX ? 0 : maxX - minX
  109. const startY = _startY === minY ? 0 : maxY - minY
  110. const endX = _endX === minX ? 0 : maxX - minX
  111. const endY = _endY === minY ? 0 : maxY - minY
  112. const path = `M${startX}, ${startY} L${endX}, ${endY}`
  113. return {
  114. svgWidth,
  115. svgHeight,
  116. startX,
  117. startY,
  118. endX,
  119. endY,
  120. path,
  121. }
  122. })
  123. const position = computed(() => {
  124. if(!start.value || !end.value) return {}
  125. const [startX, startY] = start.value
  126. const [endX, endY] = end.value
  127. const minX = Math.min(startX, endX)
  128. const maxX = Math.max(startX, endX)
  129. const minY = Math.min(startY, endY)
  130. const maxY = Math.max(startY, endY)
  131. const width = maxX - minX
  132. const height = maxY - minY
  133. return {
  134. left: minX - offset.x + 'px',
  135. top: minY - offset.y + 'px',
  136. width: width + 'px',
  137. height: height + 'px',
  138. }
  139. })
  140. return {
  141. selectionRef,
  142. start,
  143. end,
  144. creatingElement,
  145. createSelection,
  146. lineData,
  147. position,
  148. }
  149. },
  150. })
  151. </script>
  152. <style lang="scss" scoped>
  153. .element-create-selection {
  154. position: absolute;
  155. top: 0;
  156. left: 0;
  157. width: 100%;
  158. height: 100%;
  159. z-index: 2;
  160. cursor: crosshair;
  161. }
  162. .selection {
  163. position: absolute;
  164. &:not(.line) {
  165. border: 1px solid #888;
  166. }
  167. }
  168. </style>