index.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. <template>
  2. <div class="go-edit-align-line">
  3. <div
  4. class="line"
  5. v-for="item in line.lineArr"
  6. :key="item"
  7. :class="[
  8. item.includes('row') ? 'row' : 'col',
  9. line['select'].has(item) && 'visible'
  10. ]"
  11. :style="useComponentStyle(line['select'].get(item))"
  12. />
  13. </div>
  14. </template>
  15. <script setup lang="ts">
  16. import { ref, reactive, computed, watch } from 'vue'
  17. import { useDesignStore } from '@/store/modules/designStore/designStore'
  18. import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  19. import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
  20. import { CreateComponentType } from '@/packages/index.d'
  21. import throttle from 'lodash/throttle'
  22. import cloneDeep from 'lodash/cloneDeep'
  23. // 全局颜色
  24. const designStore = useDesignStore()
  25. const themeColor = ref(designStore.getAppTheme)
  26. const chartEditStore = useChartEditStore()
  27. // 线条集合
  28. const line = reactive({
  29. // 吸附距离(px)
  30. minDistance: 10,
  31. // 行横向row(上中下),列竖线col(左中右)
  32. lineArr: ['rowt', 'rowc', 'rowb', 'coll', 'colc', 'colr'],
  33. // 展示线
  34. select: new Map(),
  35. // 已经吸附
  36. sorptioned: {
  37. x: false,
  38. y: false
  39. }
  40. })
  41. // 位置计算
  42. const useComponentStyle = (attr?: Partial<{ x: number; y: number }>) => {
  43. if (!attr) return {}
  44. const componentStyle = {
  45. left: `${attr.x ? attr.x : 0}px`,
  46. top: `${attr.y ? attr.y : 0}px`
  47. }
  48. return componentStyle
  49. }
  50. // 是否开始计算
  51. const isComputedLine = computed(() => {
  52. const isDrag = chartEditStore.getEditCanvas[EditCanvasTypeEnum.IS_DRAG]
  53. return isDrag
  54. })
  55. // 吸附判定
  56. const isSorption = (selectValue: number, componentValue: number) => {
  57. const isSorption = Math.abs(selectValue - componentValue) <= line.minDistance
  58. return isSorption
  59. }
  60. // 当前目标
  61. const selectId = computed(() => chartEditStore.getTargetChart.selectId)
  62. const selectTatget = computed(
  63. () => chartEditStore.getComponentList[chartEditStore.fetchTargetIndex()]
  64. )
  65. const selectAttr = computed(() => selectTatget.value.attr)
  66. // 画布坐标
  67. const canvasPositionList = computed(() => {
  68. return {
  69. id: '0',
  70. attr: {
  71. w: cloneDeep(chartEditStore.getEditCanvasConfig.width),
  72. h: cloneDeep(chartEditStore.getEditCanvasConfig.height),
  73. x: 0,
  74. y: 0,
  75. zIndex: 0
  76. }
  77. }
  78. })
  79. // 监听鼠标移动
  80. watch(
  81. () => chartEditStore.getMousePosition,
  82. throttle(e => {
  83. if (!isComputedLine.value) return
  84. // 获取目标组件数据
  85. const selectW = selectAttr.value.w
  86. const selectH = selectAttr.value.h
  87. // 距离左侧
  88. const selectLeftX = selectAttr.value.x
  89. const selectHalfX = selectLeftX + selectW / 2
  90. const selectRightX = selectLeftX + selectW
  91. const seletX = [selectLeftX, selectHalfX, selectRightX]
  92. // 距离顶部
  93. const selectTopY = selectAttr.value.y
  94. const selectHalfY = selectTopY + selectH / 2
  95. const selectBottomY = selectTopY + selectH
  96. const seletY = [selectTopY, selectHalfY, selectBottomY]
  97. line.select.clear()
  98. line.sorptioned.y = false
  99. // 循环查询所有组件数据
  100. const componentList = chartEditStore.getComponentList.map((e:CreateComponentType) => {
  101. return {
  102. id: e.id,
  103. attr: e.attr
  104. }
  105. })
  106. componentList.push(canvasPositionList.value)
  107. // 传入画布数据
  108. line.lineArr.forEach(lineItem => {
  109. componentList.forEach((component: typeof canvasPositionList.value) => {
  110. // 排除自身
  111. if (selectId.value === component.id) return
  112. const componentW = component.attr.w
  113. const componentH = component.attr.h
  114. // 距离左侧
  115. const componentLeftX = component.attr.x
  116. const componentHalfX = componentLeftX + componentW / 2
  117. const componentRightX = componentLeftX + componentW
  118. const componentX = [componentLeftX, componentHalfX, componentRightX]
  119. // 距离顶部
  120. const componentTopY = component.attr.y
  121. const componentHalfY = componentTopY + componentH / 2
  122. const componentBottomY = componentTopY + componentH
  123. const componentY = [componentTopY, componentHalfY, componentBottomY]
  124. // 横线对比的是 Y
  125. if (lineItem.includes('rowt')) {
  126. // 顶部
  127. if (isSorption(selectTopY, componentTopY)) {
  128. line.select.set(lineItem, { y: componentTopY })
  129. selectTatget.value.setPosition(selectLeftX, componentTopY)
  130. }
  131. if (isSorption(selectTopY, componentHalfY)) {
  132. line.select.set(lineItem, { y: componentHalfY })
  133. selectTatget.value.setPosition(selectLeftX, componentHalfY)
  134. }
  135. if (isSorption(selectTopY, componentBottomY)) {
  136. line.select.set(lineItem, { y: componentBottomY })
  137. selectTatget.value.setPosition(selectLeftX, componentBottomY)
  138. }
  139. }
  140. if (lineItem.includes('rowc')) {
  141. // 顶部
  142. if (isSorption(selectHalfY, componentTopY)) {
  143. line.select.set(lineItem, { y: componentTopY })
  144. selectTatget.value.setPosition(
  145. selectLeftX,
  146. componentTopY - selectH / 2
  147. )
  148. }
  149. if (isSorption(selectHalfY, componentHalfY)) {
  150. line.select.set(lineItem, { y: componentHalfY })
  151. selectTatget.value.setPosition(
  152. selectLeftX,
  153. componentHalfY - selectH / 2
  154. )
  155. }
  156. if (isSorption(selectHalfY, componentBottomY)) {
  157. line.select.set(lineItem, { y: componentBottomY })
  158. selectTatget.value.setPosition(
  159. selectLeftX,
  160. componentBottomY - selectH / 2
  161. )
  162. }
  163. }
  164. if (lineItem.includes('rowb')) {
  165. // 顶部
  166. if (isSorption(selectBottomY, componentTopY)) {
  167. line.select.set(lineItem, { y: componentTopY })
  168. selectTatget.value.setPosition(selectLeftX, componentTopY - selectH)
  169. }
  170. if (isSorption(selectBottomY, componentHalfY)) {
  171. line.select.set(lineItem, { y: componentHalfY })
  172. selectTatget.value.setPosition(
  173. selectLeftX,
  174. componentHalfY - selectH
  175. )
  176. }
  177. if (isSorption(selectBottomY, componentBottomY)) {
  178. line.select.set(lineItem, { y: componentBottomY })
  179. selectTatget.value.setPosition(
  180. selectLeftX,
  181. componentBottomY - selectH
  182. )
  183. }
  184. }
  185. // 纵线对比的是 X
  186. if (lineItem.includes('coll')) {
  187. if (isSorption(selectLeftX, componentLeftX)) {
  188. line.select.set(lineItem, { x: componentLeftX })
  189. selectTatget.value.setPosition(componentLeftX, selectTopY)
  190. }
  191. if (isSorption(selectLeftX, componentHalfX)) {
  192. line.select.set(lineItem, { x: componentHalfX })
  193. selectTatget.value.setPosition(componentHalfX, selectTopY)
  194. }
  195. if (isSorption(selectLeftX, componentRightX)) {
  196. line.select.set(lineItem, { x: componentRightX })
  197. selectTatget.value.setPosition(componentRightX, selectTopY)
  198. }
  199. }
  200. if (lineItem.includes('colc')) {
  201. if (isSorption(selectHalfX, componentLeftX)) {
  202. line.select.set(lineItem, { x: componentLeftX })
  203. selectTatget.value.setPosition(
  204. componentLeftX - selectW / 2,
  205. selectTopY
  206. )
  207. }
  208. if (isSorption(selectHalfX, componentHalfX)) {
  209. line.select.set(lineItem, { x: componentHalfX })
  210. selectTatget.value.setPosition(
  211. componentHalfX - selectW / 2,
  212. selectTopY
  213. )
  214. }
  215. if (isSorption(selectHalfX, componentRightX)) {
  216. line.select.set(lineItem, { x: componentRightX })
  217. selectTatget.value.setPosition(
  218. componentRightX - selectW / 2,
  219. selectTopY
  220. )
  221. }
  222. }
  223. if (lineItem.includes('colr')) {
  224. if (isSorption(selectRightX, componentLeftX)) {
  225. line.select.set(lineItem, { x: componentLeftX })
  226. selectTatget.value.setPosition(componentLeftX - selectW, selectTopY)
  227. }
  228. if (isSorption(selectRightX, componentHalfX)) {
  229. line.select.set(lineItem, { x: componentHalfX })
  230. selectTatget.value.setPosition(componentHalfX - selectW, selectTopY)
  231. }
  232. if (isSorption(selectRightX, componentRightX)) {
  233. line.select.set(lineItem, { x: componentRightX })
  234. selectTatget.value.setPosition( componentRightX - selectW, selectTopY )
  235. }
  236. }
  237. /*
  238. * 我也不知道为什么这个不行,还没时间调
  239. if(lineItem.includes('row')) {
  240. seletY.forEach(sY => {
  241. componentY.forEach(cY => {
  242. if (isSorption(sY, cY)) {
  243. line.select.set(lineItem, { y: cY })
  244. }
  245. })
  246. })
  247. return
  248. }
  249. if(lineItem.includes('col')) {
  250. seletX.forEach(sX => {
  251. componentX.forEach(cX => {
  252. if (isSorption(sX, cX)) {
  253. line.select.set(lineItem, { x: sX })
  254. }
  255. })
  256. })
  257. return
  258. }
  259. */
  260. })
  261. })
  262. }, 200),
  263. {
  264. deep: true
  265. }
  266. )
  267. // 取消对齐线
  268. watch(
  269. () => isComputedLine.value,
  270. (val: boolean) => {
  271. if (!val) {
  272. line.select.clear()
  273. line.sorptioned.y = false
  274. chartEditStore.setEditCanvas(EditCanvasTypeEnum.IS_DRAG, true)
  275. }
  276. }
  277. )
  278. </script>
  279. <style lang="scss" scoped>
  280. @include go(edit-align-line) {
  281. .line {
  282. z-index: 1;
  283. position: absolute;
  284. top: 0;
  285. left: 0;
  286. display: none;
  287. border-width: 1px;
  288. border-style: solid;
  289. border-color: v-bind('themeColor');
  290. opacity: 0.7;
  291. &.visible {
  292. display: block;
  293. }
  294. }
  295. .row {
  296. width: 100%;
  297. }
  298. .col {
  299. height: 100%;
  300. }
  301. }
  302. </style>