align.ts 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. import { ExcalidrawElement } from "./element/types";
  2. import { newElementWith } from "./element/mutateElement";
  3. import { Box, getCommonBoundingBox } from "./element/bounds";
  4. import { getMaximumGroups } from "./groups";
  5. export interface Alignment {
  6. position: "start" | "center" | "end";
  7. axis: "x" | "y";
  8. }
  9. export const alignElements = (
  10. selectedElements: ExcalidrawElement[],
  11. alignment: Alignment,
  12. ): ExcalidrawElement[] => {
  13. const groups: ExcalidrawElement[][] = getMaximumGroups(selectedElements);
  14. const selectionBoundingBox = getCommonBoundingBox(selectedElements);
  15. return groups.flatMap((group) => {
  16. const translation = calculateTranslation(
  17. group,
  18. selectionBoundingBox,
  19. alignment,
  20. );
  21. return group.map((element) =>
  22. newElementWith(element, {
  23. x: element.x + translation.x,
  24. y: element.y + translation.y,
  25. }),
  26. );
  27. });
  28. };
  29. const calculateTranslation = (
  30. group: ExcalidrawElement[],
  31. selectionBoundingBox: Box,
  32. { axis, position }: Alignment,
  33. ): { x: number; y: number } => {
  34. const groupBoundingBox = getCommonBoundingBox(group);
  35. const [min, max]: ["minX" | "minY", "maxX" | "maxY"] =
  36. axis === "x" ? ["minX", "maxX"] : ["minY", "maxY"];
  37. const noTranslation = { x: 0, y: 0 };
  38. if (position === "start") {
  39. return {
  40. ...noTranslation,
  41. [axis]: selectionBoundingBox[min] - groupBoundingBox[min],
  42. };
  43. } else if (position === "end") {
  44. return {
  45. ...noTranslation,
  46. [axis]: selectionBoundingBox[max] - groupBoundingBox[max],
  47. };
  48. } // else if (position === "center") {
  49. return {
  50. ...noTranslation,
  51. [axis]:
  52. (selectionBoundingBox[min] + selectionBoundingBox[max]) / 2 -
  53. (groupBoundingBox[min] + groupBoundingBox[max]) / 2,
  54. };
  55. };