From 926b4f24e684ffaa29aad66303883e8933f01eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnay=20Mert=20Karado=C4=9Fan?= Date: Thu, 23 Jan 2020 09:21:04 +0000 Subject: [PATCH] Draw horizontal/vertical lines/arrows when shift pressed (#430) * Draw horizontal/vertical lines/arrows when shift pressed * Refactor resizing with delta * Resize arrows/lines perfectly when shift pressed --- src/element/index.ts | 6 ++- src/element/sizeChecks.ts | 5 -- src/element/sizeHelpers.ts | 59 ++++++++++++++++++++++ src/index.tsx | 101 ++++++++++++++++++++++++------------- 4 files changed, 129 insertions(+), 42 deletions(-) delete mode 100644 src/element/sizeChecks.ts create mode 100644 src/element/sizeHelpers.ts diff --git a/src/element/index.ts b/src/element/index.ts index 51932d16..45ab30ef 100644 --- a/src/element/index.ts +++ b/src/element/index.ts @@ -12,4 +12,8 @@ export { resizeTest, getCursorForResizingElement } from "./resizeTest"; export { isTextElement } from "./typeChecks"; export { textWysiwyg } from "./textWysiwyg"; export { redrawTextBoundingBox } from "./textElement"; -export { isInvisiblySmallElement } from "./sizeChecks"; +export { + getPerfectElementSize, + isInvisiblySmallElement, + resizePerfectLineForNWHandler +} from "./sizeHelpers"; diff --git a/src/element/sizeChecks.ts b/src/element/sizeChecks.ts deleted file mode 100644 index 01cf9209..00000000 --- a/src/element/sizeChecks.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { ExcalidrawElement } from "./types"; - -export function isInvisiblySmallElement(element: ExcalidrawElement): boolean { - return element.width === 0 && element.height === 0; -} diff --git a/src/element/sizeHelpers.ts b/src/element/sizeHelpers.ts new file mode 100644 index 00000000..9773cff3 --- /dev/null +++ b/src/element/sizeHelpers.ts @@ -0,0 +1,59 @@ +import { ExcalidrawElement } from "./types"; + +export function isInvisiblySmallElement(element: ExcalidrawElement): boolean { + return element.width === 0 && element.height === 0; +} + +/** + * Makes a perfect shape or diagonal/horizontal/vertical line + */ +export function getPerfectElementSize( + elementType: string, + width: number, + height: number +): { width: number; height: number } { + const absWidth = Math.abs(width); + const absHeight = Math.abs(height); + + if (elementType === "line" || elementType === "arrow") { + if (absHeight < absWidth / 2) { + height = 0; + } else if (absWidth < absHeight / 2) { + width = 0; + } else { + height = absWidth * Math.sign(height); + } + } else if (elementType !== "selection") { + height = absWidth * Math.sign(height); + } + + return { width, height }; +} + +export function resizePerfectLineForNWHandler( + element: ExcalidrawElement, + x: number, + y: number +) { + const anchorX = element.x + element.width; + const anchorY = element.y + element.height; + const distanceToAnchorX = x - anchorX; + const distanceToAnchorY = y - anchorY; + if (Math.abs(distanceToAnchorX) < Math.abs(distanceToAnchorY) / 2) { + element.x = anchorX; + element.width = 0; + element.y = y; + element.height = -distanceToAnchorY; + } else if (Math.abs(distanceToAnchorY) < Math.abs(element.width) / 2) { + element.y = anchorY; + element.height = 0; + } else { + element.x = x; + element.width = -distanceToAnchorX; + element.height = + Math.sign(distanceToAnchorY) * + Math.sign(distanceToAnchorX) * + element.width; + element.y = anchorY - element.height; + } +} diff --git a/src/index.tsx b/src/index.tsx index 45b21349..b091426b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -13,7 +13,9 @@ import { isTextElement, textWysiwyg, getElementAbsoluteCoords, - getCursorForResizingElement + getCursorForResizingElement, + getPerfectElementSize, + resizePerfectLineForNWHandler } from "./element"; import { clearSelection, @@ -940,67 +942,83 @@ export class App extends React.Component { const selectedElements = elements.filter(el => el.isSelected); if (selectedElements.length === 1) { const { x, y } = viewportCoordsToSceneCoords(e, this.state); - let deltaX = 0; - let deltaY = 0; + const deltaX = x - lastX; + const deltaY = y - lastY; const element = selectedElements[0]; switch (resizeHandle) { case "nw": - deltaX = lastX - x; - element.width += deltaX; - element.x -= deltaX; + element.width -= deltaX; + element.x += deltaX; + if (e.shiftKey) { - element.y += element.height - element.width; - element.height = element.width; + if ( + element.type === "arrow" || + element.type === "line" + ) { + resizePerfectLineForNWHandler(element, x, y); + } else { + element.y += element.height - element.width; + element.height = element.width; + } } else { - const deltaY = lastY - y; - element.height += deltaY; - element.y -= deltaY; + element.height -= deltaY; + element.y += deltaY; } break; case "ne": - element.width += x - lastX; + element.width += deltaX; if (e.shiftKey) { element.y += element.height - element.width; element.height = element.width; } else { - deltaY = lastY - y; - element.height += deltaY; - element.y -= deltaY; + element.height -= deltaY; + element.y += deltaY; } break; case "sw": - deltaX = lastX - x; - element.width += deltaX; - element.x -= deltaX; + element.width -= deltaX; + element.x += deltaX; if (e.shiftKey) { element.height = element.width; } else { - element.height += y - lastY; + element.height += deltaY; } break; case "se": - element.width += x - lastX; if (e.shiftKey) { - element.height = element.width; + if ( + element.type === "arrow" || + element.type === "line" + ) { + const { width, height } = getPerfectElementSize( + element.type, + x - element.x, + y - element.y + ); + element.width = width; + element.height = height; + } else { + element.width += deltaX; + element.height = element.width; + } } else { - element.height += y - lastY; + element.width += deltaX; + element.height += deltaY; } break; case "n": - deltaY = lastY - y; - element.height += deltaY; - element.y -= deltaY; + element.height -= deltaY; + element.y += deltaY; break; case "w": - deltaX = lastX - x; - element.width += deltaX; - element.x -= deltaX; + element.width -= deltaX; + element.x += deltaX; break; case "s": - element.height += y - lastY; + element.height += deltaY; break; case "e": - element.width += x - lastX; + element.width += deltaX; break; } @@ -1056,12 +1074,23 @@ export class App extends React.Component { CANVAS_WINDOW_OFFSET_TOP - draggingElement.y - this.state.scrollY; - draggingElement.width = width; - // Make a perfect square or circle when shift is enabled - draggingElement.height = - e.shiftKey && this.state.elementType !== "selection" - ? Math.abs(width) * Math.sign(height) - : height; + + if (e.shiftKey) { + let { + width: newWidth, + height: newHeight + } = getPerfectElementSize( + this.state.elementType, + width, + height + ); + draggingElement.width = newWidth; + draggingElement.height = newHeight; + } else { + draggingElement.width = width; + draggingElement.height = height; + } + draggingElement.shape = null; if (this.state.elementType === "selection") {