mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Resize multiple elements (rectangles, diamonds and ellipses onl… (#1193)
* experiment resizing multiple elements * hack common component * calculate scale properly (still se only)fg * prioritize multi selection * take handle offset into calculation * fix master merge * refactor resizeElements out from App * wip: handlerRectanglesFromCoords * fix test with type assertion * properly show handles wip * revert previous one and do a tweak * remove unnecessary assignments * replace hack code with good one * refactor coords in arg * resize NW * resize from sw,ne * fix with setResizeHandle * do not show hint while resizing multiple elements * empty commit * fix format
This commit is contained in:
parent
b60f5fcf06
commit
2cc1105ff5
9 changed files with 808 additions and 510 deletions
|
@ -13,6 +13,14 @@ const handleSizes: { [k in PointerType]: number } = {
|
|||
|
||||
const ROTATION_HANDLER_GAP = 16;
|
||||
|
||||
export const OMIT_SIDES_FOR_MULTIPLE_ELEMENTS = {
|
||||
e: true,
|
||||
s: true,
|
||||
n: true,
|
||||
w: true,
|
||||
rotation: true,
|
||||
};
|
||||
|
||||
function generateHandler(
|
||||
x: number,
|
||||
y: number,
|
||||
|
@ -26,11 +34,13 @@ function generateHandler(
|
|||
return [xx - width / 2, yy - height / 2, width, height];
|
||||
}
|
||||
|
||||
export function handlerRectangles(
|
||||
element: ExcalidrawElement,
|
||||
export function handlerRectanglesFromCoords(
|
||||
[x1, y1, x2, y2]: [number, number, number, number],
|
||||
angle: number,
|
||||
zoom: number,
|
||||
pointerType: PointerType = "mouse",
|
||||
) {
|
||||
omitSides: { [T in Sides]?: boolean } = {},
|
||||
): Partial<{ [T in Sides]: [number, number, number, number] }> {
|
||||
const size = handleSizes[pointerType];
|
||||
const handlerWidth = size / zoom;
|
||||
const handlerHeight = size / zoom;
|
||||
|
@ -38,116 +48,145 @@ export function handlerRectangles(
|
|||
const handlerMarginX = size / zoom;
|
||||
const handlerMarginY = size / zoom;
|
||||
|
||||
const [elementX1, elementY1, elementX2, elementY2] = getElementAbsoluteCoords(
|
||||
element,
|
||||
);
|
||||
|
||||
const elementWidth = elementX2 - elementX1;
|
||||
const elementHeight = elementY2 - elementY1;
|
||||
const cx = (elementX1 + elementX2) / 2;
|
||||
const cy = (elementY1 + elementY2) / 2;
|
||||
const angle = element.angle;
|
||||
const width = x2 - x1;
|
||||
const height = y2 - y1;
|
||||
const cx = (x1 + x2) / 2;
|
||||
const cy = (y1 + y2) / 2;
|
||||
|
||||
const dashedLineMargin = 4 / zoom;
|
||||
|
||||
const centeringOffset = (size - 8) / (2 * zoom);
|
||||
|
||||
const handlers =
|
||||
{
|
||||
nw: generateHandler(
|
||||
elementX1 - dashedLineMargin - handlerMarginX + centeringOffset,
|
||||
elementY1 - dashedLineMargin - handlerMarginY + centeringOffset,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
),
|
||||
ne: generateHandler(
|
||||
elementX2 + dashedLineMargin - centeringOffset,
|
||||
elementY1 - dashedLineMargin - handlerMarginY + centeringOffset,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
),
|
||||
sw: generateHandler(
|
||||
elementX1 - dashedLineMargin - handlerMarginX + centeringOffset,
|
||||
elementY2 + dashedLineMargin - centeringOffset,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
),
|
||||
se: generateHandler(
|
||||
elementX2 + dashedLineMargin - centeringOffset,
|
||||
elementY2 + dashedLineMargin - centeringOffset,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
),
|
||||
rotation: generateHandler(
|
||||
elementX1 + elementWidth / 2 - handlerWidth / 2,
|
||||
elementY1 -
|
||||
dashedLineMargin -
|
||||
handlerMarginY +
|
||||
centeringOffset -
|
||||
ROTATION_HANDLER_GAP / zoom,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
),
|
||||
} as { [T in Sides]: [number, number, number, number] };
|
||||
const handlers: Partial<
|
||||
{ [T in Sides]: [number, number, number, number] }
|
||||
> = {
|
||||
nw: omitSides["nw"]
|
||||
? undefined
|
||||
: generateHandler(
|
||||
x1 - dashedLineMargin - handlerMarginX + centeringOffset,
|
||||
y1 - dashedLineMargin - handlerMarginY + centeringOffset,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
),
|
||||
ne: omitSides["ne"]
|
||||
? undefined
|
||||
: generateHandler(
|
||||
x2 + dashedLineMargin - centeringOffset,
|
||||
y1 - dashedLineMargin - handlerMarginY + centeringOffset,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
),
|
||||
sw: omitSides["sw"]
|
||||
? undefined
|
||||
: generateHandler(
|
||||
x1 - dashedLineMargin - handlerMarginX + centeringOffset,
|
||||
y2 + dashedLineMargin - centeringOffset,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
),
|
||||
se: omitSides["se"]
|
||||
? undefined
|
||||
: generateHandler(
|
||||
x2 + dashedLineMargin - centeringOffset,
|
||||
y2 + dashedLineMargin - centeringOffset,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
),
|
||||
rotation: omitSides["rotation"]
|
||||
? undefined
|
||||
: generateHandler(
|
||||
x1 + width / 2 - handlerWidth / 2,
|
||||
y1 -
|
||||
dashedLineMargin -
|
||||
handlerMarginY +
|
||||
centeringOffset -
|
||||
ROTATION_HANDLER_GAP / zoom,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
),
|
||||
};
|
||||
|
||||
// We only want to show height handlers (all cardinal directions) above a certain size
|
||||
const minimumSizeForEightHandlers = (5 * size) / zoom;
|
||||
if (Math.abs(elementWidth) > minimumSizeForEightHandlers) {
|
||||
handlers["n"] = generateHandler(
|
||||
elementX1 + elementWidth / 2 - handlerWidth / 2,
|
||||
elementY1 - dashedLineMargin - handlerMarginY + centeringOffset,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
);
|
||||
handlers["s"] = generateHandler(
|
||||
elementX1 + elementWidth / 2 - handlerWidth / 2,
|
||||
elementY2 + dashedLineMargin - centeringOffset,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
);
|
||||
if (Math.abs(width) > minimumSizeForEightHandlers) {
|
||||
if (!omitSides["n"]) {
|
||||
handlers["n"] = generateHandler(
|
||||
x1 + width / 2 - handlerWidth / 2,
|
||||
y1 - dashedLineMargin - handlerMarginY + centeringOffset,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
);
|
||||
}
|
||||
if (!omitSides["s"]) {
|
||||
handlers["s"] = generateHandler(
|
||||
x1 + width / 2 - handlerWidth / 2,
|
||||
y2 + dashedLineMargin - centeringOffset,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (Math.abs(elementHeight) > minimumSizeForEightHandlers) {
|
||||
handlers["w"] = generateHandler(
|
||||
elementX1 - dashedLineMargin - handlerMarginX + centeringOffset,
|
||||
elementY1 + elementHeight / 2 - handlerHeight / 2,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
);
|
||||
handlers["e"] = generateHandler(
|
||||
elementX2 + dashedLineMargin - centeringOffset,
|
||||
elementY1 + elementHeight / 2 - handlerHeight / 2,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
);
|
||||
if (Math.abs(height) > minimumSizeForEightHandlers) {
|
||||
if (!omitSides["w"]) {
|
||||
handlers["w"] = generateHandler(
|
||||
x1 - dashedLineMargin - handlerMarginX + centeringOffset,
|
||||
y1 + height / 2 - handlerHeight / 2,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
);
|
||||
}
|
||||
if (!omitSides["e"]) {
|
||||
handlers["e"] = generateHandler(
|
||||
x2 + dashedLineMargin - centeringOffset,
|
||||
y1 + height / 2 - handlerHeight / 2,
|
||||
handlerWidth,
|
||||
handlerHeight,
|
||||
cx,
|
||||
cy,
|
||||
angle,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return handlers;
|
||||
}
|
||||
|
||||
export function handlerRectangles(
|
||||
element: ExcalidrawElement,
|
||||
zoom: number,
|
||||
pointerType: PointerType = "mouse",
|
||||
) {
|
||||
const handlers = handlerRectanglesFromCoords(
|
||||
getElementAbsoluteCoords(element),
|
||||
element.angle,
|
||||
zoom,
|
||||
pointerType,
|
||||
);
|
||||
|
||||
if (element.type === "arrow" || element.type === "line") {
|
||||
if (element.points.length === 2) {
|
||||
// only check the last point because starting point is always (0,0)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue