mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-04-14 16:40:58 -04:00
* rotate rectanble with fixed angle * rotate dashed rectangle with fixed angle * fix rotate handler rect * fix canvas size with rotation * angle in element base * fix bug in calculating canvas size * trial only for rectangle * hitTest for rectangle rotation * properly resize rotated rectangle * fix canvas size calculation * giving up... workaround for now * **experimental** handler to rotate rectangle * remove rotation on copy for debugging * update snapshots * better rotation handler with atan2 * rotate when drawImage * add rotation handler * hitTest for any shapes * fix hitTest for curved lines * rotate text element * rotation locking * hint messaage for rotating * show proper handlers on mobile (a workaround, there should be a better way) * refactor hitTest * support exporting png * support exporting svg * fix rotating curved line * refactor drawElementFromCanvas with getElementAbsoluteCoords * fix export png and svg * adjust resize positions for lines (N, E, S, W) * do not make handlers big on mobile * Update src/locales/en.json Alright! Co-Authored-By: Lipis <lipiridis@gmail.com> * do not show rotation/resizing hints on mobile * proper calculation for N and W positions * simplify calculation * use "rotation" as property name for clarification (may increase bundle size) * update snapshots excluding rotation handle * refactor with adjustPositionWithRotation * refactor with adjustXYWithRotation * forgot to rename rotation * rename internal function * initialize element angle on restore * rotate wysiwyg editor * fix shift-rotate around 270deg * improve rotation locking * refactor adjustXYWithRotation * avoid rotation degree becomes >=360 * refactor with generateHandler Co-authored-by: Lipis <lipiridis@gmail.com> Co-authored-by: dwelle <luzar.david@gmail.com>
177 lines
3.9 KiB
TypeScript
177 lines
3.9 KiB
TypeScript
import { ExcalidrawElement, PointerType } from "./types";
|
|
|
|
import { handlerRectangles } from "./handlerRectangles";
|
|
import { AppState } from "../types";
|
|
import { isLinearElement } from "./typeChecks";
|
|
|
|
type HandlerRectanglesRet = keyof ReturnType<typeof handlerRectangles>;
|
|
|
|
function isInHandlerRect(
|
|
handler: [number, number, number, number],
|
|
x: number,
|
|
y: number,
|
|
) {
|
|
return (
|
|
x >= handler[0] &&
|
|
x <= handler[0] + handler[2] &&
|
|
y >= handler[1] &&
|
|
y <= handler[1] + handler[3]
|
|
);
|
|
}
|
|
|
|
export function resizeTest(
|
|
element: ExcalidrawElement,
|
|
appState: AppState,
|
|
x: number,
|
|
y: number,
|
|
zoom: number,
|
|
pointerType: PointerType,
|
|
): HandlerRectanglesRet | false {
|
|
if (!appState.selectedElementIds[element.id]) {
|
|
return false;
|
|
}
|
|
|
|
const { rotation: rotationHandler, ...handlers } = handlerRectangles(
|
|
element,
|
|
zoom,
|
|
pointerType,
|
|
);
|
|
|
|
if (rotationHandler && isInHandlerRect(rotationHandler, x, y)) {
|
|
return "rotation" as HandlerRectanglesRet;
|
|
}
|
|
|
|
if (element.type === "text") {
|
|
// can't resize text elements
|
|
return false;
|
|
}
|
|
|
|
const filter = Object.keys(handlers).filter((key) => {
|
|
const handler = handlers[key as Exclude<HandlerRectanglesRet, "rotation">]!;
|
|
if (!handler) {
|
|
return false;
|
|
}
|
|
return isInHandlerRect(handler, x, y);
|
|
});
|
|
|
|
if (filter.length > 0) {
|
|
return filter[0] as HandlerRectanglesRet;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
export function getElementWithResizeHandler(
|
|
elements: readonly ExcalidrawElement[],
|
|
appState: AppState,
|
|
{ x, y }: { x: number; y: number },
|
|
zoom: number,
|
|
pointerType: PointerType,
|
|
) {
|
|
return elements.reduce((result, element) => {
|
|
if (result) {
|
|
return result;
|
|
}
|
|
const resizeHandle = resizeTest(element, appState, x, y, zoom, pointerType);
|
|
return resizeHandle ? { element, resizeHandle } : null;
|
|
}, null as { element: ExcalidrawElement; resizeHandle: ReturnType<typeof resizeTest> } | null);
|
|
}
|
|
|
|
/*
|
|
* Returns bi-directional cursor for the element being resized
|
|
*/
|
|
export function getCursorForResizingElement(resizingElement: {
|
|
element: ExcalidrawElement;
|
|
resizeHandle: ReturnType<typeof resizeTest>;
|
|
}): string {
|
|
const { element, resizeHandle } = resizingElement;
|
|
const shouldSwapCursors =
|
|
Math.sign(element.height) * Math.sign(element.width) === -1;
|
|
let cursor = null;
|
|
|
|
switch (resizeHandle) {
|
|
case "n":
|
|
case "s":
|
|
cursor = "ns";
|
|
break;
|
|
case "w":
|
|
case "e":
|
|
cursor = "ew";
|
|
break;
|
|
case "nw":
|
|
case "se":
|
|
if (shouldSwapCursors) {
|
|
cursor = "nesw";
|
|
} else {
|
|
cursor = "nwse";
|
|
}
|
|
break;
|
|
case "ne":
|
|
case "sw":
|
|
if (shouldSwapCursors) {
|
|
cursor = "nwse";
|
|
} else {
|
|
cursor = "nesw";
|
|
}
|
|
break;
|
|
case "rotation":
|
|
cursor = "ew";
|
|
break;
|
|
}
|
|
|
|
return cursor ? `${cursor}-resize` : "";
|
|
}
|
|
|
|
export function normalizeResizeHandle(
|
|
element: ExcalidrawElement,
|
|
resizeHandle: HandlerRectanglesRet,
|
|
): HandlerRectanglesRet {
|
|
if ((element.width >= 0 && element.height >= 0) || isLinearElement(element)) {
|
|
return resizeHandle;
|
|
}
|
|
|
|
if (element.width < 0 && element.height < 0) {
|
|
switch (resizeHandle) {
|
|
case "nw":
|
|
return "se";
|
|
case "ne":
|
|
return "sw";
|
|
case "se":
|
|
return "nw";
|
|
case "sw":
|
|
return "ne";
|
|
}
|
|
} else if (element.width < 0) {
|
|
switch (resizeHandle) {
|
|
case "nw":
|
|
return "ne";
|
|
case "ne":
|
|
return "nw";
|
|
case "se":
|
|
return "sw";
|
|
case "sw":
|
|
return "se";
|
|
case "e":
|
|
return "w";
|
|
case "w":
|
|
return "e";
|
|
}
|
|
} else {
|
|
switch (resizeHandle) {
|
|
case "nw":
|
|
return "sw";
|
|
case "ne":
|
|
return "se";
|
|
case "se":
|
|
return "ne";
|
|
case "sw":
|
|
return "nw";
|
|
case "n":
|
|
return "s";
|
|
case "s":
|
|
return "n";
|
|
}
|
|
}
|
|
|
|
return resizeHandle;
|
|
}
|