useAlignActiveElement.ts 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import { storeToRefs } from 'pinia'
  2. import { useMainStore, useSlidesStore } from '@/store'
  3. import type { PPTElement } from '@/types/slides'
  4. import { ElementAlignCommands } from '@/types/edit'
  5. import { getElementListRange, getRectRotatedOffset } from '@/utils/element'
  6. import useHistorySnapshot from './useHistorySnapshot'
  7. interface RangeMap {
  8. [id: string]: ReturnType<typeof getElementListRange>
  9. }
  10. export default () => {
  11. const slidesStore = useSlidesStore()
  12. const { activeElementIdList, activeElementList } = storeToRefs(useMainStore())
  13. const { currentSlide } = storeToRefs(slidesStore)
  14. const { addHistorySnapshot } = useHistorySnapshot()
  15. /**
  16. * 对齐选中的元素
  17. * @param command 对齐方向
  18. */
  19. const alignActiveElement = (command: ElementAlignCommands) => {
  20. const { minX, maxX, minY, maxY } = getElementListRange(activeElementList.value)
  21. const elementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
  22. // 如果所选择的元素为组合元素的成员,需要计算该组合的整体范围
  23. const groupElementRangeMap: RangeMap = {}
  24. for (const activeElement of activeElementList.value) {
  25. if (activeElement.groupId && !groupElementRangeMap[activeElement.groupId]) {
  26. const groupElements = activeElementList.value.filter(item => item.groupId === activeElement.groupId)
  27. groupElementRangeMap[activeElement.groupId] = getElementListRange(groupElements)
  28. }
  29. }
  30. // 根据不同的命令,计算对齐的位置
  31. if (command === ElementAlignCommands.LEFT) {
  32. elementList.forEach(element => {
  33. if (activeElementIdList.value.includes(element.id)) {
  34. if (!element.groupId) {
  35. if ('rotate' in element && element.rotate) {
  36. const { offsetX } = getRectRotatedOffset({
  37. left: element.left,
  38. top: element.top,
  39. width: element.width,
  40. height: element.height,
  41. rotate: element.rotate,
  42. })
  43. element.left = minX - offsetX
  44. }
  45. else element.left = minX
  46. }
  47. else {
  48. const range = groupElementRangeMap[element.groupId]
  49. const offset = range.minX - minX
  50. element.left = element.left - offset
  51. }
  52. }
  53. })
  54. }
  55. else if (command === ElementAlignCommands.RIGHT) {
  56. elementList.forEach(element => {
  57. if (activeElementIdList.value.includes(element.id)) {
  58. if (!element.groupId) {
  59. const elWidth = element.type === 'line' ? Math.max(element.start[0], element.end[0]) : element.width
  60. if ('rotate' in element && element.rotate) {
  61. const { offsetX } = getRectRotatedOffset({
  62. left: element.left,
  63. top: element.top,
  64. width: element.width,
  65. height: element.height,
  66. rotate: element.rotate,
  67. })
  68. element.left = maxX - elWidth + offsetX
  69. }
  70. else element.left = maxX - elWidth
  71. }
  72. else {
  73. const range = groupElementRangeMap[element.groupId]
  74. const offset = range.maxX - maxX
  75. element.left = element.left - offset
  76. }
  77. }
  78. })
  79. }
  80. else if (command === ElementAlignCommands.TOP) {
  81. elementList.forEach(element => {
  82. if (activeElementIdList.value.includes(element.id)) {
  83. if (!element.groupId) {
  84. if ('rotate' in element && element.rotate) {
  85. const { offsetY } = getRectRotatedOffset({
  86. left: element.left,
  87. top: element.top,
  88. width: element.width,
  89. height: element.height,
  90. rotate: element.rotate,
  91. })
  92. element.top = minY - offsetY
  93. }
  94. else element.top = minY
  95. }
  96. else {
  97. const range = groupElementRangeMap[element.groupId]
  98. const offset = range.minY - minY
  99. element.top = element.top - offset
  100. }
  101. }
  102. })
  103. }
  104. else if (command === ElementAlignCommands.BOTTOM) {
  105. elementList.forEach(element => {
  106. if (activeElementIdList.value.includes(element.id)) {
  107. if (!element.groupId) {
  108. const elHeight = element.type === 'line' ? Math.max(element.start[1], element.end[1]) : element.height
  109. if ('rotate' in element && element.rotate) {
  110. const { offsetY } = getRectRotatedOffset({
  111. left: element.left,
  112. top: element.top,
  113. width: element.width,
  114. height: element.height,
  115. rotate: element.rotate,
  116. })
  117. element.top = maxY - elHeight + offsetY
  118. }
  119. else element.top = maxY - elHeight
  120. }
  121. else {
  122. const range = groupElementRangeMap[element.groupId]
  123. const offset = range.maxY - maxY
  124. element.top = element.top - offset
  125. }
  126. }
  127. })
  128. }
  129. else if (command === ElementAlignCommands.HORIZONTAL) {
  130. const horizontalCenter = (minX + maxX) / 2
  131. elementList.forEach(element => {
  132. if (activeElementIdList.value.includes(element.id)) {
  133. if (!element.groupId) {
  134. const elWidth = element.type === 'line' ? Math.max(element.start[0], element.end[0]) : element.width
  135. element.left = horizontalCenter - elWidth / 2
  136. }
  137. else {
  138. const range = groupElementRangeMap[element.groupId]
  139. const center = (range.maxX + range.minX) / 2
  140. const offset = center - horizontalCenter
  141. element.left = element.left - offset
  142. }
  143. }
  144. })
  145. }
  146. else if (command === ElementAlignCommands.VERTICAL) {
  147. const verticalCenter = (minY + maxY) / 2
  148. elementList.forEach(element => {
  149. if (activeElementIdList.value.includes(element.id)) {
  150. if (!element.groupId) {
  151. const elHeight = element.type === 'line' ? Math.max(element.start[1], element.end[1]) : element.height
  152. element.top = verticalCenter - elHeight / 2
  153. }
  154. else {
  155. const range = groupElementRangeMap[element.groupId]
  156. const center = (range.maxY + range.minY) / 2
  157. const offset = center - verticalCenter
  158. element.top = element.top - offset
  159. }
  160. }
  161. })
  162. }
  163. slidesStore.updateSlide({ elements: elementList })
  164. addHistorySnapshot()
  165. }
  166. return {
  167. alignActiveElement,
  168. }
  169. }