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.

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