feat: resize elements from the sides (#7855)

Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
Ryan Di 2024-05-01 00:05:00 +08:00 committed by GitHub
parent 6e5aeb112d
commit 88812e0386
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 3913 additions and 3741 deletions

View file

@ -6,15 +6,24 @@ import {
} from "./types";
import {
OMIT_SIDES_FOR_MULTIPLE_ELEMENTS,
getTransformHandlesFromCoords,
getTransformHandles,
TransformHandleType,
TransformHandle,
MaybeTransformHandleType,
getOmitSidesForDevice,
canResizeFromSides,
} from "./transformHandles";
import { AppState, Zoom } from "../types";
import { Bounds } from "./bounds";
import { AppState, Device, Zoom } from "../types";
import { Bounds, getElementAbsoluteCoords } from "./bounds";
import { SIDE_RESIZING_THRESHOLD } from "../constants";
import {
angleToDegrees,
pointOnLine,
pointRotate,
} from "../../utils/geometry/geometry";
import { Line, Point } from "../../utils/geometry/shape";
import { isLinearElement } from "./typeChecks";
const isInsideTransformHandle = (
transformHandle: TransformHandle,
@ -34,13 +43,20 @@ export const resizeTest = (
y: number,
zoom: Zoom,
pointerType: PointerType,
device: Device,
): MaybeTransformHandleType => {
if (!appState.selectedElementIds[element.id]) {
return false;
}
const { rotation: rotationTransformHandle, ...transformHandles } =
getTransformHandles(element, zoom, elementsMap, pointerType);
getTransformHandles(
element,
zoom,
elementsMap,
pointerType,
getOmitSidesForDevice(device),
);
if (
rotationTransformHandle &&
@ -62,6 +78,35 @@ export const resizeTest = (
return filter[0] as TransformHandleType;
}
if (canResizeFromSides(device)) {
const [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords(
element,
elementsMap,
);
// Note that for a text element, when "resized" from the side
// we should make it wrap/unwrap
if (
element.type !== "text" &&
!(isLinearElement(element) && element.points.length <= 2)
) {
const SPACING = SIDE_RESIZING_THRESHOLD / zoom.value;
const sides = getSelectionBorders(
[x1 - SPACING, y1 - SPACING],
[x2 + SPACING, y2 + SPACING],
[cx, cy],
angleToDegrees(element.angle),
);
for (const [dir, side] of Object.entries(sides)) {
// test to see if x, y are on the line segment
if (pointOnLine([x, y], side as Line, SPACING)) {
return dir as TransformHandleType;
}
}
}
}
return false;
};
@ -73,6 +118,7 @@ export const getElementWithTransformHandleType = (
zoom: Zoom,
pointerType: PointerType,
elementsMap: ElementsMap,
device: Device,
) => {
return elements.reduce((result, element) => {
if (result) {
@ -86,6 +132,7 @@ export const getElementWithTransformHandleType = (
scenePointerY,
zoom,
pointerType,
device,
);
return transformHandleType ? { element, transformHandleType } : null;
}, null as { element: NonDeletedExcalidrawElement; transformHandleType: MaybeTransformHandleType } | null);
@ -97,13 +144,14 @@ export const getTransformHandleTypeFromCoords = (
scenePointerY: number,
zoom: Zoom,
pointerType: PointerType,
device: Device,
): MaybeTransformHandleType => {
const transformHandles = getTransformHandlesFromCoords(
[x1, y1, x2, y2, (x1 + x2) / 2, (y1 + y2) / 2],
0,
zoom,
pointerType,
OMIT_SIDES_FOR_MULTIPLE_ELEMENTS,
getOmitSidesForDevice(device),
);
const found = Object.keys(transformHandles).find((key) => {
@ -114,7 +162,33 @@ export const getTransformHandleTypeFromCoords = (
isInsideTransformHandle(transformHandle, scenePointerX, scenePointerY)
);
});
return (found || false) as MaybeTransformHandleType;
if (found) {
return found as MaybeTransformHandleType;
}
if (canResizeFromSides(device)) {
const cx = (x1 + x2) / 2;
const cy = (y1 + y2) / 2;
const SPACING = SIDE_RESIZING_THRESHOLD / zoom.value;
const sides = getSelectionBorders(
[x1 - SPACING, y1 - SPACING],
[x2 + SPACING, y2 + SPACING],
[cx, cy],
angleToDegrees(0),
);
for (const [dir, side] of Object.entries(sides)) {
// test to see if x, y are on the line segment
if (pointOnLine([scenePointerX, scenePointerY], side as Line, SPACING)) {
return dir as TransformHandleType;
}
}
}
return false;
};
const RESIZE_CURSORS = ["ns", "nesw", "ew", "nwse"];
@ -174,3 +248,22 @@ export const getCursorForResizingElement = (resizingElement: {
return cursor ? `${cursor}-resize` : "";
};
const getSelectionBorders = (
[x1, y1]: Point,
[x2, y2]: Point,
center: Point,
angleInDegrees: number,
) => {
const topLeft = pointRotate([x1, y1], angleInDegrees, center);
const topRight = pointRotate([x2, y1], angleInDegrees, center);
const bottomLeft = pointRotate([x1, y2], angleInDegrees, center);
const bottomRight = pointRotate([x2, y2], angleInDegrees, center);
return {
n: [topLeft, topRight],
e: [topRight, bottomRight],
s: [bottomRight, bottomLeft],
w: [bottomLeft, topLeft],
};
};