Browse Source

Draw horizontal/vertical lines/arrows when shift pressed (#430)

* Draw horizontal/vertical lines/arrows when shift pressed

* Refactor resizing with delta

* Resize arrows/lines perfectly when shift pressed
Günay Mert Karadoğan 5 years ago
parent
commit
926b4f24e6
4 changed files with 129 additions and 42 deletions
  1. 5 1
      src/element/index.ts
  2. 0 5
      src/element/sizeChecks.ts
  3. 59 0
      src/element/sizeHelpers.ts
  4. 65 36
      src/index.tsx

+ 5 - 1
src/element/index.ts

@@ -12,4 +12,8 @@ export { resizeTest, getCursorForResizingElement } from "./resizeTest";
 export { isTextElement } from "./typeChecks";
 export { isTextElement } from "./typeChecks";
 export { textWysiwyg } from "./textWysiwyg";
 export { textWysiwyg } from "./textWysiwyg";
 export { redrawTextBoundingBox } from "./textElement";
 export { redrawTextBoundingBox } from "./textElement";
-export { isInvisiblySmallElement } from "./sizeChecks";
+export {
+  getPerfectElementSize,
+  isInvisiblySmallElement,
+  resizePerfectLineForNWHandler
+} from "./sizeHelpers";

+ 0 - 5
src/element/sizeChecks.ts

@@ -1,5 +0,0 @@
-import { ExcalidrawElement } from "./types";
-
-export function isInvisiblySmallElement(element: ExcalidrawElement): boolean {
-  return element.width === 0 && element.height === 0;
-}

+ 59 - 0
src/element/sizeHelpers.ts

@@ -0,0 +1,59 @@
+import { ExcalidrawElement } from "./types";
+
+export function isInvisiblySmallElement(element: ExcalidrawElement): boolean {
+  return element.width === 0 && element.height === 0;
+}
+
+/**
+ * Makes a perfect shape or diagonal/horizontal/vertical line
+ */
+export function getPerfectElementSize(
+  elementType: string,
+  width: number,
+  height: number
+): { width: number; height: number } {
+  const absWidth = Math.abs(width);
+  const absHeight = Math.abs(height);
+
+  if (elementType === "line" || elementType === "arrow") {
+    if (absHeight < absWidth / 2) {
+      height = 0;
+    } else if (absWidth < absHeight / 2) {
+      width = 0;
+    } else {
+      height = absWidth * Math.sign(height);
+    }
+  } else if (elementType !== "selection") {
+    height = absWidth * Math.sign(height);
+  }
+
+  return { width, height };
+}
+
+export function resizePerfectLineForNWHandler(
+  element: ExcalidrawElement,
+  x: number,
+  y: number
+) {
+  const anchorX = element.x + element.width;
+  const anchorY = element.y + element.height;
+  const distanceToAnchorX = x - anchorX;
+  const distanceToAnchorY = y - anchorY;
+  if (Math.abs(distanceToAnchorX) < Math.abs(distanceToAnchorY) / 2) {
+    element.x = anchorX;
+    element.width = 0;
+    element.y = y;
+    element.height = -distanceToAnchorY;
+  } else if (Math.abs(distanceToAnchorY) < Math.abs(element.width) / 2) {
+    element.y = anchorY;
+    element.height = 0;
+  } else {
+    element.x = x;
+    element.width = -distanceToAnchorX;
+    element.height =
+      Math.sign(distanceToAnchorY) *
+      Math.sign(distanceToAnchorX) *
+      element.width;
+    element.y = anchorY - element.height;
+  }
+}

+ 65 - 36
src/index.tsx

@@ -13,7 +13,9 @@ import {
   isTextElement,
   isTextElement,
   textWysiwyg,
   textWysiwyg,
   getElementAbsoluteCoords,
   getElementAbsoluteCoords,
-  getCursorForResizingElement
+  getCursorForResizingElement,
+  getPerfectElementSize,
+  resizePerfectLineForNWHandler
 } from "./element";
 } from "./element";
 import {
 import {
   clearSelection,
   clearSelection,
@@ -940,67 +942,83 @@ export class App extends React.Component<any, AppState> {
                 const selectedElements = elements.filter(el => el.isSelected);
                 const selectedElements = elements.filter(el => el.isSelected);
                 if (selectedElements.length === 1) {
                 if (selectedElements.length === 1) {
                   const { x, y } = viewportCoordsToSceneCoords(e, this.state);
                   const { x, y } = viewportCoordsToSceneCoords(e, this.state);
-                  let deltaX = 0;
-                  let deltaY = 0;
+                  const deltaX = x - lastX;
+                  const deltaY = y - lastY;
                   const element = selectedElements[0];
                   const element = selectedElements[0];
                   switch (resizeHandle) {
                   switch (resizeHandle) {
                     case "nw":
                     case "nw":
-                      deltaX = lastX - x;
-                      element.width += deltaX;
-                      element.x -= deltaX;
+                      element.width -= deltaX;
+                      element.x += deltaX;
+
                       if (e.shiftKey) {
                       if (e.shiftKey) {
-                        element.y += element.height - element.width;
-                        element.height = element.width;
+                        if (
+                          element.type === "arrow" ||
+                          element.type === "line"
+                        ) {
+                          resizePerfectLineForNWHandler(element, x, y);
+                        } else {
+                          element.y += element.height - element.width;
+                          element.height = element.width;
+                        }
                       } else {
                       } else {
-                        const deltaY = lastY - y;
-                        element.height += deltaY;
-                        element.y -= deltaY;
+                        element.height -= deltaY;
+                        element.y += deltaY;
                       }
                       }
                       break;
                       break;
                     case "ne":
                     case "ne":
-                      element.width += x - lastX;
+                      element.width += deltaX;
                       if (e.shiftKey) {
                       if (e.shiftKey) {
                         element.y += element.height - element.width;
                         element.y += element.height - element.width;
                         element.height = element.width;
                         element.height = element.width;
                       } else {
                       } else {
-                        deltaY = lastY - y;
-                        element.height += deltaY;
-                        element.y -= deltaY;
+                        element.height -= deltaY;
+                        element.y += deltaY;
                       }
                       }
                       break;
                       break;
                     case "sw":
                     case "sw":
-                      deltaX = lastX - x;
-                      element.width += deltaX;
-                      element.x -= deltaX;
+                      element.width -= deltaX;
+                      element.x += deltaX;
                       if (e.shiftKey) {
                       if (e.shiftKey) {
                         element.height = element.width;
                         element.height = element.width;
                       } else {
                       } else {
-                        element.height += y - lastY;
+                        element.height += deltaY;
                       }
                       }
                       break;
                       break;
                     case "se":
                     case "se":
-                      element.width += x - lastX;
                       if (e.shiftKey) {
                       if (e.shiftKey) {
-                        element.height = element.width;
+                        if (
+                          element.type === "arrow" ||
+                          element.type === "line"
+                        ) {
+                          const { width, height } = getPerfectElementSize(
+                            element.type,
+                            x - element.x,
+                            y - element.y
+                          );
+                          element.width = width;
+                          element.height = height;
+                        } else {
+                          element.width += deltaX;
+                          element.height = element.width;
+                        }
                       } else {
                       } else {
-                        element.height += y - lastY;
+                        element.width += deltaX;
+                        element.height += deltaY;
                       }
                       }
                       break;
                       break;
                     case "n":
                     case "n":
-                      deltaY = lastY - y;
-                      element.height += deltaY;
-                      element.y -= deltaY;
+                      element.height -= deltaY;
+                      element.y += deltaY;
                       break;
                       break;
                     case "w":
                     case "w":
-                      deltaX = lastX - x;
-                      element.width += deltaX;
-                      element.x -= deltaX;
+                      element.width -= deltaX;
+                      element.x += deltaX;
                       break;
                       break;
                     case "s":
                     case "s":
-                      element.height += y - lastY;
+                      element.height += deltaY;
                       break;
                       break;
                     case "e":
                     case "e":
-                      element.width += x - lastX;
+                      element.width += deltaX;
                       break;
                       break;
                   }
                   }
 
 
@@ -1056,12 +1074,23 @@ export class App extends React.Component<any, AppState> {
                 CANVAS_WINDOW_OFFSET_TOP -
                 CANVAS_WINDOW_OFFSET_TOP -
                 draggingElement.y -
                 draggingElement.y -
                 this.state.scrollY;
                 this.state.scrollY;
-              draggingElement.width = width;
-              // Make a perfect square or circle when shift is enabled
-              draggingElement.height =
-                e.shiftKey && this.state.elementType !== "selection"
-                  ? Math.abs(width) * Math.sign(height)
-                  : height;
+
+              if (e.shiftKey) {
+                let {
+                  width: newWidth,
+                  height: newHeight
+                } = getPerfectElementSize(
+                  this.state.elementType,
+                  width,
+                  height
+                );
+                draggingElement.width = newWidth;
+                draggingElement.height = newHeight;
+              } else {
+                draggingElement.width = width;
+                draggingElement.height = height;
+              }
+
               draggingElement.shape = null;
               draggingElement.shape = null;
 
 
               if (this.state.elementType === "selection") {
               if (this.state.elementType === "selection") {