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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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).translateTo(shape, [
  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).translateTo(shape, [x, bounds.minY])
  63. x += bounds.width + step
  64. }
  65. }
  66. break
  67. }
  68. case DistributeType.Vertical: {
  69. const span = entries.reduce((a, c) => a + c[1].height, 0)
  70. if (span > commonBounds.height) {
  71. const top = entries.sort((a, b) => a[1].minY - b[1].minY)[0]
  72. const bottom = entries.sort((a, b) => b[1].maxY - a[1].maxY)[0]
  73. const entriesToMove = entries
  74. .filter((a) => a !== top && a !== bottom)
  75. .sort((a, b) => centers[a[0]][1] - centers[b[0]][1])
  76. const step =
  77. (centers[bottom[0]][1] - centers[top[0]][1]) / (len - 1)
  78. const y = centers[top[0]][1] + step
  79. for (let i = 0; i < entriesToMove.length; i++) {
  80. const [id, bounds] = entriesToMove[i]
  81. const shape = shapes[id]
  82. getShapeUtils(shape).translateTo(shape, [
  83. bounds.minX,
  84. y + step * i - bounds.height / 2,
  85. ])
  86. }
  87. } else {
  88. const entriesToMove = entries.sort(
  89. (a, b) => centers[a[0]][1] - centers[b[0]][1]
  90. )
  91. let y = commonBounds.minY
  92. const step = (commonBounds.height - span) / (len - 1)
  93. for (let i = 0; i < entriesToMove.length - 1; i++) {
  94. const [id, bounds] = entriesToMove[i]
  95. const shape = shapes[id]
  96. getShapeUtils(shape).translateTo(shape, [bounds.minX, y])
  97. y += bounds.height + step
  98. }
  99. }
  100. break
  101. }
  102. }
  103. },
  104. undo(data) {
  105. const { shapes } = getPage(data, currentPageId)
  106. for (let id in boundsForShapes) {
  107. const shape = shapes[id]
  108. const initialBounds = boundsForShapes[id]
  109. getShapeUtils(shape).translateTo(shape, [
  110. initialBounds.minX,
  111. initialBounds.minY,
  112. ])
  113. }
  114. },
  115. })
  116. )
  117. }