You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

distribute.ts 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import Command from './command'
  2. import history from '../history'
  3. import { Data, DistributeType } from 'types'
  4. import {
  5. getBoundsCenter,
  6. getCommonBounds,
  7. getPage,
  8. getSelectedShapes,
  9. } from 'utils/utils'
  10. import { getShapeUtils } from 'state/shape-utils'
  11. export default function distributeCommand(
  12. data: Data,
  13. type: DistributeType
  14. ): void {
  15. const { currentPageId } = data
  16. const selectedShapes = getSelectedShapes(data).filter(
  17. (shape) => !shape.isLocked
  18. )
  19. const entries = selectedShapes.map(
  20. (shape) => [shape.id, getShapeUtils(shape).getBounds(shape)] as const
  21. )
  22. const boundsForShapes = Object.fromEntries(entries)
  23. const commonBounds = getCommonBounds(...entries.map((entry) => entry[1]))
  24. const centers = Object.fromEntries(
  25. selectedShapes.map((shape) => [
  26. shape.id,
  27. getBoundsCenter(boundsForShapes[shape.id]),
  28. ])
  29. )
  30. history.execute(
  31. data,
  32. new Command({
  33. name: 'distribute_shapes',
  34. category: 'canvas',
  35. do(data) {
  36. const { shapes } = getPage(data, currentPageId)
  37. const len = entries.length
  38. switch (type) {
  39. case DistributeType.Horizontal: {
  40. const span = entries.reduce((a, c) => a + c[1].width, 0)
  41. if (span > commonBounds.width) {
  42. const left = entries.sort((a, b) => a[1].minX - b[1].minX)[0]
  43. const right = entries.sort((a, b) => b[1].maxX - a[1].maxX)[0]
  44. const entriesToMove = entries
  45. .filter((a) => a !== left && a !== right)
  46. .sort((a, b) => centers[a[0]][0] - centers[b[0]][0])
  47. const step =
  48. (centers[right[0]][0] - centers[left[0]][0]) / (len - 1)
  49. const x = centers[left[0]][0] + step
  50. for (let i = 0; i < entriesToMove.length; i++) {
  51. const [id, bounds] = entriesToMove[i]
  52. const shape = shapes[id]
  53. getShapeUtils(shape).translateTo(shape, [
  54. x + step * i - bounds.width / 2,
  55. bounds.minY,
  56. ])
  57. }
  58. } else {
  59. const entriesToMove = entries.sort(
  60. (a, b) => centers[a[0]][0] - centers[b[0]][0]
  61. )
  62. let x = commonBounds.minX
  63. const step = (commonBounds.width - span) / (len - 1)
  64. for (let i = 0; i < entriesToMove.length - 1; i++) {
  65. const [id, bounds] = entriesToMove[i]
  66. const shape = shapes[id]
  67. getShapeUtils(shape).translateTo(shape, [x, bounds.minY])
  68. x += bounds.width + step
  69. }
  70. }
  71. break
  72. }
  73. case DistributeType.Vertical: {
  74. const span = entries.reduce((a, c) => a + c[1].height, 0)
  75. if (span > commonBounds.height) {
  76. const top = entries.sort((a, b) => a[1].minY - b[1].minY)[0]
  77. const bottom = entries.sort((a, b) => b[1].maxY - a[1].maxY)[0]
  78. const entriesToMove = entries
  79. .filter((a) => a !== top && a !== bottom)
  80. .sort((a, b) => centers[a[0]][1] - centers[b[0]][1])
  81. const step =
  82. (centers[bottom[0]][1] - centers[top[0]][1]) / (len - 1)
  83. const y = centers[top[0]][1] + step
  84. for (let i = 0; i < entriesToMove.length; i++) {
  85. const [id, bounds] = entriesToMove[i]
  86. const shape = shapes[id]
  87. getShapeUtils(shape).translateTo(shape, [
  88. bounds.minX,
  89. y + step * i - bounds.height / 2,
  90. ])
  91. }
  92. } else {
  93. const entriesToMove = entries.sort(
  94. (a, b) => centers[a[0]][1] - centers[b[0]][1]
  95. )
  96. let y = commonBounds.minY
  97. const step = (commonBounds.height - span) / (len - 1)
  98. for (let i = 0; i < entriesToMove.length - 1; i++) {
  99. const [id, bounds] = entriesToMove[i]
  100. const shape = shapes[id]
  101. getShapeUtils(shape).translateTo(shape, [bounds.minX, y])
  102. y += bounds.height + step
  103. }
  104. }
  105. break
  106. }
  107. }
  108. },
  109. undo(data) {
  110. const { shapes } = getPage(data, currentPageId)
  111. for (const id in boundsForShapes) {
  112. const shape = shapes[id]
  113. const initialBounds = boundsForShapes[id]
  114. getShapeUtils(shape).translateTo(shape, [
  115. initialBounds.minX,
  116. initialBounds.minY,
  117. ])
  118. }
  119. },
  120. })
  121. )
  122. }