فهرست منبع

Refactor: resize two-point lines/arrows (#1568)

Daishi Kato 5 سال پیش
والد
کامیت
394237728f
4فایلهای تغییر یافته به همراه99 افزوده شده و 184 حذف شده
  1. 24 29
      src/components/App.tsx
  2. 1 0
      src/element/index.ts
  3. 74 145
      src/element/resizeElements.ts
  4. 0 10
      src/element/types.ts

+ 24 - 29
src/components/App.tsx

@@ -25,6 +25,7 @@ import {
   getElementWithResizeHandler,
   canResizeMutlipleElements,
   getResizeOffsetXY,
+  getResizeArrowDirection,
   getResizeHandlerFromCoords,
   isNonDeletedElement,
 } from "../element";
@@ -53,11 +54,7 @@ import Portal from "./Portal";
 
 import { renderScene } from "../renderer";
 import { AppState, GestureEvent, Gesture } from "../types";
-import {
-  ExcalidrawElement,
-  ExcalidrawTextElement,
-  ResizeArrowFnType,
-} from "../element/types";
+import { ExcalidrawElement, ExcalidrawTextElement } from "../element/types";
 
 import { distance2d, isPathALoop } from "../math";
 
@@ -1847,6 +1844,7 @@ class App extends React.Component<any, AppState> {
       resizeHandle = nextResizeHandle;
     };
     let resizeOffsetXY: [number, number] = [0, 0];
+    let resizeArrowDirection: "origin" | "end" = "origin";
     let isResizingElements = false;
     let draggingOccurred = false;
     let hitElement: ExcalidrawElement | null = null;
@@ -1900,6 +1898,16 @@ class App extends React.Component<any, AppState> {
           x,
           y,
         );
+        if (
+          selectedElements.length === 1 &&
+          isLinearElement(selectedElements[0]) &&
+          selectedElements[0].points.length === 2
+        ) {
+          resizeArrowDirection = getResizeArrowDirection(
+            resizeHandle,
+            selectedElements[0],
+          );
+        }
       }
       if (!isResizingElements) {
         hitElement = getElementAtPosition(
@@ -2079,11 +2087,6 @@ class App extends React.Component<any, AppState> {
       }
     }
 
-    let resizeArrowFn: ResizeArrowFnType | null = null;
-    const setResizeArrowFn = (fn: ResizeArrowFnType) => {
-      resizeArrowFn = fn;
-    };
-
     let selectedElementWasDuplicated = false;
 
     const onPointerMove = withBatchedUpdates((event: PointerEvent) => {
@@ -2142,23 +2145,17 @@ class App extends React.Component<any, AppState> {
           isResizing: resizeHandle && resizeHandle !== "rotation",
           isRotating: resizeHandle === "rotation",
         });
-        const resized = resizeElements(
-          resizeHandle,
-          setResizeHandle,
-          selectedElements,
-          resizeArrowFn,
-          setResizeArrowFn,
-          event,
-          x,
-          y,
-          resizeOffsetXY[0],
-          resizeOffsetXY[1],
-          lastX,
-          lastY,
-        );
-        if (resized) {
-          lastX = x;
-          lastY = y;
+        if (
+          resizeElements(
+            resizeHandle,
+            setResizeHandle,
+            selectedElements,
+            resizeArrowDirection,
+            event,
+            x - resizeOffsetXY[0],
+            y - resizeOffsetXY[1],
+          )
+        ) {
           return;
         }
       }
@@ -2328,8 +2325,6 @@ class App extends React.Component<any, AppState> {
 
       this.savePointer(childEvent.clientX, childEvent.clientY, "up");
 
-      resizeArrowFn = null;
-      resizeOffsetXY = [0, 0];
       lastPointerUp = null;
 
       window.removeEventListener(EVENT.POINTER_MOVE, onPointerMove);

+ 1 - 0
src/element/index.ts

@@ -36,6 +36,7 @@ export {
   resizeElements,
   canResizeMutlipleElements,
   getResizeOffsetXY,
+  getResizeArrowDirection,
 } from "./resizeElements";
 export { isTextElement, isExcalidrawElement } from "./typeChecks";
 export { textWysiwyg } from "./textWysiwyg";

+ 74 - 145
src/element/resizeElements.ts

@@ -6,7 +6,6 @@ import {
   ExcalidrawLinearElement,
   NonDeletedExcalidrawElement,
   NonDeleted,
-  ResizeArrowFnType,
 } from "./types";
 import {
   getElementAbsoluteCoords,
@@ -32,15 +31,10 @@ export const resizeElements = (
   resizeHandle: ResizeTestType,
   setResizeHandle: (nextResizeHandle: ResizeTestType) => void,
   selectedElements: NonDeletedExcalidrawElement[],
-  resizeArrowFn: ResizeArrowFnType | null, // XXX eliminate in #1339
-  setResizeArrowFn: (fn: ResizeArrowFnType) => void, // XXX eliminate in #1339
+  resizeArrowDirection: "origin" | "end",
   event: PointerEvent, // XXX we want to make it independent?
   pointerX: number,
   pointerY: number,
-  offsetX: number,
-  offsetY: number,
-  lastX: number, // XXX eliminate in #1339
-  lastY: number, // XXX eliminate in #1339
 ) => {
   if (selectedElements.length === 1) {
     const [element] = selectedElements;
@@ -56,14 +50,10 @@ export const resizeElements = (
     ) {
       resizeSingleTwoPointElement(
         element,
-        resizeHandle,
-        resizeArrowFn,
-        setResizeArrowFn,
+        resizeArrowDirection,
         event.shiftKey,
         pointerX,
         pointerY,
-        lastX,
-        lastY,
       );
     } else if (resizeHandle) {
       resizeSingleElement(
@@ -73,8 +63,6 @@ export const resizeElements = (
         getResizeCenterPointKey(event),
         pointerX,
         pointerY,
-        offsetX,
-        offsetY,
       );
       setResizeHandle(normalizeResizeHandle(element, resizeHandle));
       if (element.width < 0) {
@@ -100,14 +88,7 @@ export const resizeElements = (
       resizeHandle === "sw" ||
       resizeHandle === "se")
   ) {
-    resizeMultipleElements(
-      selectedElements,
-      resizeHandle,
-      pointerX,
-      pointerY,
-      offsetX,
-      offsetY,
-    );
+    resizeMultipleElements(selectedElements, resizeHandle, pointerX, pointerY);
     return true;
   }
   return false;
@@ -135,122 +116,61 @@ const rotateSingleElement = (
 
 const resizeSingleTwoPointElement = (
   element: NonDeleted<ExcalidrawLinearElement>,
-  resizeHandle: "nw" | "ne" | "sw" | "se",
-  resizeArrowFn: ResizeArrowFnType | null,
-  setResizeArrowFn: (fn: ResizeArrowFnType) => void,
-  sidesWithSameLength: boolean,
+  resizeArrowDirection: "origin" | "end",
+  isAngleLocking: boolean,
   pointerX: number,
   pointerY: number,
-  lastX: number,
-  lastY: number,
 ) => {
-  const [, [px, py]] = element.points;
-  const isResizeEnd =
-    (resizeHandle === "nw" && (px < 0 || py < 0)) ||
-    (resizeHandle === "ne" && px >= 0) ||
-    (resizeHandle === "sw" && px <= 0) ||
-    (resizeHandle === "se" && (px > 0 || py > 0));
-  applyResizeArrowFn(
-    element,
-    resizeArrowFn,
-    setResizeArrowFn,
-    isResizeEnd,
-    sidesWithSameLength,
-    pointerX,
-    pointerY,
-    lastX,
-    lastY,
-  );
-};
-
-const arrowResizeOrigin: ResizeArrowFnType = (
-  element,
-  pointIndex,
-  deltaX,
-  deltaY,
-  pointerX,
-  pointerY,
-  sidesWithSameLength,
-) => {
-  const [px, py] = element.points[pointIndex];
-  let x = element.x + deltaX;
-  let y = element.y + deltaY;
-  let pointX = px - deltaX;
-  let pointY = py - deltaY;
-
-  if (sidesWithSameLength) {
-    const { width, height } = getPerfectElementSize(
-      element.type,
-      px + element.x - pointerX,
-      py + element.y - pointerY,
-    );
-    x = px + element.x - width;
-    y = py + element.y - height;
-    pointX = width;
-    pointY = height;
-  }
-
-  mutateElement(element, {
-    x,
-    y,
-    points: element.points.map((point, i) =>
-      i === pointIndex ? ([pointX, pointY] as const) : point,
-    ),
-  });
-};
-
-const arrowResizeEnd: ResizeArrowFnType = (
-  element,
-  pointIndex,
-  deltaX,
-  deltaY,
-  pointerX,
-  pointerY,
-  sidesWithSameLength,
-) => {
-  const [px, py] = element.points[pointIndex];
-  if (sidesWithSameLength) {
-    const { width, height } = getPerfectElementSize(
-      element.type,
-      pointerX - element.x,
-      pointerY - element.y,
-    );
-    mutateElement(element, {
-      points: element.points.map((point, i) =>
-        i === pointIndex ? ([width, height] as const) : point,
-      ),
-    });
+  const pointOrigin = element.points[0]; // can assume always [0, 0]?
+  const pointEnd = element.points[1];
+  if (resizeArrowDirection === "end") {
+    if (isAngleLocking) {
+      const { width, height } = getPerfectElementSize(
+        element.type,
+        pointerX - element.x,
+        pointerY - element.y,
+      );
+      mutateElement(element, {
+        points: [pointOrigin, [width, height]],
+      });
+    } else {
+      mutateElement(element, {
+        points: [
+          pointOrigin,
+          [
+            pointerX - pointOrigin[0] - element.x,
+            pointerY - pointOrigin[1] - element.y,
+          ],
+        ],
+      });
+    }
   } else {
-    mutateElement(element, {
-      points: element.points.map((point, i) =>
-        i === pointIndex ? ([px + deltaX, py + deltaY] as const) : point,
-      ),
-    });
-  }
-};
-
-const applyResizeArrowFn = (
-  element: NonDeleted<ExcalidrawLinearElement>,
-  resizeArrowFn: ResizeArrowFnType | null,
-  setResizeArrowFn: (fn: ResizeArrowFnType) => void,
-  isResizeEnd: boolean,
-  sidesWithSameLength: boolean,
-  x: number,
-  y: number,
-  lastX: number,
-  lastY: number,
-) => {
-  const angle = element.angle;
-  const [deltaX, deltaY] = rotate(x - lastX, y - lastY, 0, 0, -angle);
-  if (!resizeArrowFn) {
-    if (isResizeEnd) {
-      resizeArrowFn = arrowResizeEnd;
+    // resizeArrowDirection === "origin"
+    if (isAngleLocking) {
+      const { width, height } = getPerfectElementSize(
+        element.type,
+        element.x + pointEnd[0] - pointOrigin[0] - pointerX,
+        element.y + pointEnd[1] - pointOrigin[1] - pointerY,
+      );
+      mutateElement(element, {
+        x: element.x + pointEnd[0] - pointOrigin[0] - width,
+        y: element.y + pointEnd[1] - pointOrigin[1] - height,
+        points: [pointOrigin, [width, height]],
+      });
     } else {
-      resizeArrowFn = arrowResizeOrigin;
+      mutateElement(element, {
+        x: pointerX,
+        y: pointerY,
+        points: [
+          pointOrigin,
+          [
+            pointEnd[0] - (pointerX - pointOrigin[0] - element.x),
+            pointEnd[1] - (pointerY - pointOrigin[1] - element.y),
+          ],
+        ],
+      });
     }
   }
-  resizeArrowFn(element, 1, deltaX, deltaY, x, y, sidesWithSameLength);
-  setResizeArrowFn(resizeArrowFn);
 };
 
 const resizeSingleElement = (
@@ -260,16 +180,14 @@ const resizeSingleElement = (
   isResizeFromCenter: boolean,
   pointerX: number,
   pointerY: number,
-  offsetX: number,
-  offsetY: number,
 ) => {
   const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
   const cx = (x1 + x2) / 2;
   const cy = (y1 + y2) / 2;
   // rotation pointer with reverse angle
   const [rotatedX, rotatedY] = rotate(
-    pointerX - offsetX,
-    pointerY - offsetY,
+    pointerX,
+    pointerY,
     cx,
     cy,
     -element.angle,
@@ -366,15 +284,13 @@ const resizeMultipleElements = (
   resizeHandle: "nw" | "ne" | "sw" | "se",
   pointerX: number,
   pointerY: number,
-  offsetX: number,
-  offsetY: number,
 ) => {
   const [x1, y1, x2, y2] = getCommonBounds(elements);
   switch (resizeHandle) {
     case "se": {
       const scale = Math.max(
-        (pointerX - offsetX - x1) / (x2 - x1),
-        (pointerY - offsetY - y1) / (y2 - y1),
+        (pointerX - x1) / (x2 - x1),
+        (pointerY - y1) / (y2 - y1),
       );
       if (scale > 0) {
         elements.forEach((element) => {
@@ -389,8 +305,8 @@ const resizeMultipleElements = (
     }
     case "nw": {
       const scale = Math.max(
-        (x2 - (pointerX - offsetX)) / (x2 - x1),
-        (y2 - (pointerY - offsetY)) / (y2 - y1),
+        (x2 - pointerX) / (x2 - x1),
+        (y2 - pointerY) / (y2 - y1),
       );
       if (scale > 0) {
         elements.forEach((element) => {
@@ -405,8 +321,8 @@ const resizeMultipleElements = (
     }
     case "ne": {
       const scale = Math.max(
-        (pointerX - offsetX - x1) / (x2 - x1),
-        (y2 - (pointerY - offsetY)) / (y2 - y1),
+        (pointerX - x1) / (x2 - x1),
+        (y2 - pointerY) / (y2 - y1),
       );
       if (scale > 0) {
         elements.forEach((element) => {
@@ -421,8 +337,8 @@ const resizeMultipleElements = (
     }
     case "sw": {
       const scale = Math.max(
-        (x2 - (pointerX - offsetX)) / (x2 - x1),
-        (pointerY - offsetY - y1) / (y2 - y1),
+        (x2 - pointerX) / (x2 - x1),
+        (pointerY - y1) / (y2 - y1),
       );
       if (scale > 0) {
         elements.forEach((element) => {
@@ -481,3 +397,16 @@ export const getResizeOffsetXY = (
       return [0, 0];
   }
 };
+
+export const getResizeArrowDirection = (
+  resizeHandle: ResizeTestType,
+  element: NonDeleted<ExcalidrawLinearElement>,
+): "origin" | "end" => {
+  const [, [px, py]] = element.points;
+  const isResizeEnd =
+    (resizeHandle === "nw" && (px < 0 || py < 0)) ||
+    (resizeHandle === "ne" && px >= 0) ||
+    (resizeHandle === "sw" && px <= 0) ||
+    (resizeHandle === "se" && (px > 0 || py > 0));
+  return isResizeEnd ? "end" : "origin";
+};

+ 0 - 10
src/element/types.ts

@@ -58,13 +58,3 @@ export type ExcalidrawLinearElement = _ExcalidrawElementBase &
 export type PointerType = "mouse" | "pen" | "touch";
 
 export type TextAlign = "left" | "center" | "right";
-
-export type ResizeArrowFnType = (
-  element: NonDeleted<ExcalidrawLinearElement>,
-  pointIndex: number,
-  deltaX: number,
-  deltaY: number,
-  pointerX: number,
-  pointerY: number,
-  perfect: boolean,
-) => void;