Refactor: resize two-point lines/arrows (#1568)
This commit is contained in:
parent
cdb483b895
commit
394237728f
@ -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);
|
||||
|
@ -36,6 +36,7 @@ export {
|
||||
resizeElements,
|
||||
canResizeMutlipleElements,
|
||||
getResizeOffsetXY,
|
||||
getResizeArrowDirection,
|
||||
} from "./resizeElements";
|
||||
export { isTextElement, isExcalidrawElement } from "./typeChecks";
|
||||
export { textWysiwyg } from "./textWysiwyg";
|
||||
|
@ -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,
|
||||
),
|
||||
});
|
||||
} 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;
|
||||
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 {
|
||||
resizeArrowFn = arrowResizeOrigin;
|
||||
mutateElement(element, {
|
||||
points: [
|
||||
pointOrigin,
|
||||
[
|
||||
pointerX - pointOrigin[0] - element.x,
|
||||
pointerY - pointOrigin[1] - element.y,
|
||||
],
|
||||
],
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 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 {
|
||||
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";
|
||||
};
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user