From 1ea72e91345db1e048bc99a10fc4c421287a8e45 Mon Sep 17 00:00:00 2001 From: Faustino Kialungila Date: Thu, 9 Jan 2020 12:34:46 +0100 Subject: [PATCH] Center element on paste (#248) * Center element on paste * paste on cursor position * correctly center elements * rename vars --- README.md | 1 - src/index.tsx | 97 +++++++++++++++++++++++++++++----------- src/scene/comparisons.ts | 4 +- src/types.ts | 2 + 4 files changed, 75 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 2deb6220..c9458a50 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ Go to https://www.excalidraw.com to start sketching - ## Run the code ### Code Sandbox diff --git a/src/index.tsx b/src/index.tsx index 9e503b4a..1f4163dc 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -10,7 +10,8 @@ import { duplicateElement, resizeTest, isTextElement, - textWysiwyg + textWysiwyg, + getElementAbsoluteCoords } from "./element"; import { clearSelection, @@ -115,6 +116,7 @@ export class App extends React.Component<{}, AppState> { public componentDidMount() { document.addEventListener("keydown", this.onKeyDown, false); + document.addEventListener("mousemove", this.getCurrentCursorPosition); window.addEventListener("resize", this.onResize, false); const savedState = restoreFromLocalStorage(elements); @@ -125,6 +127,11 @@ export class App extends React.Component<{}, AppState> { public componentWillUnmount() { document.removeEventListener("keydown", this.onKeyDown, false); + document.removeEventListener( + "mousemove", + this.getCurrentCursorPosition, + false + ); window.removeEventListener("resize", this.onResize, false); } @@ -139,6 +146,8 @@ export class App extends React.Component<{}, AppState> { viewBackgroundColor: "#ffffff", scrollX: 0, scrollY: 0, + cursorX: 0, + cursorY: 0, name: DEFAULT_PROJECT_NAME }; @@ -146,6 +155,10 @@ export class App extends React.Component<{}, AppState> { this.forceUpdate(); }; + private getCurrentCursorPosition = (e: MouseEvent) => { + this.setState({ cursorX: e.x, cursorY: e.y }); + }; + private onKeyDown = (event: KeyboardEvent) => { if (isInputLike(event.target)) return; @@ -263,7 +276,7 @@ export class App extends React.Component<{}, AppState> { element.fillStyle = pastedElement?.fillStyle; element.opacity = pastedElement?.opacity; element.roughness = pastedElement?.roughness; - if(isTextElement(element)) { + if (isTextElement(element)) { element.font = pastedElement?.font; this.redrawTextBoundingBox(element); } @@ -331,11 +344,11 @@ export class App extends React.Component<{}, AppState> { } }; - private pasteFromClipboard = (x?: number, y?: number) => { + private pasteFromClipboard = () => { if (navigator.clipboard) { navigator.clipboard .readText() - .then(text => this.addElementsFromPaste(text, x, y)); + .then(text => this.addElementsFromPaste(text)); } }; @@ -345,7 +358,7 @@ export class App extends React.Component<{}, AppState> { element.height = metrics.height; element.baseline = metrics.baseline; this.forceUpdate(); - } + }; public render() { const canvasWidth = window.innerWidth - CANVAS_WINDOW_OFFSET_LEFT; @@ -480,39 +493,45 @@ export class App extends React.Component<{}, AppState> {
Font size
isTextElement(element) && +element.font.split("px ")[0] + element => + isTextElement(element) && +element.font.split("px ")[0] )} onChange={value => this.changeProperty(element => { - if(isTextElement(element)) { - element.font = `${value}px ${element.font.split("px ")[1]}`; + if (isTextElement(element)) { + element.font = `${value}px ${ + element.font.split("px ")[1] + }`; this.redrawTextBoundingBox(element); } }) } />
Font familly
- isTextElement(element) && element.font.split("px ")[1] + element => + isTextElement(element) && element.font.split("px ")[1] )} onChange={value => this.changeProperty(element => { - if(isTextElement(element)) { - element.font = `${element.font.split("px ")[0]}px ${value}`; + if (isTextElement(element)) { + element.font = `${ + element.font.split("px ")[0] + }px ${value}`; this.redrawTextBoundingBox(element); } }) @@ -609,7 +628,7 @@ export class App extends React.Component<{}, AppState> { options: [ navigator.clipboard && { label: "Paste", - action: () => this.pasteFromClipboard(x, y) + action: () => this.pasteFromClipboard() } ], top: e.clientY, @@ -632,7 +651,7 @@ export class App extends React.Component<{}, AppState> { }, navigator.clipboard && { label: "Paste", - action: () => this.pasteFromClipboard(x, y) + action: () => this.pasteFromClipboard() }, { label: "Copy Styles", action: this.copyStyles }, { label: "Paste Styles", action: this.pasteStyles }, @@ -1105,7 +1124,7 @@ export class App extends React.Component<{}, AppState> { })); }; - private addElementsFromPaste = (paste: string, x?: number, y?: number) => { + private addElementsFromPaste = (paste: string) => { let parsedElements; try { parsedElements = JSON.parse(paste); @@ -1117,19 +1136,47 @@ export class App extends React.Component<{}, AppState> { ) { clearSelection(elements); - if (x == null) x = 10 - this.state.scrollX; - if (y == null) y = 10 - this.state.scrollY; + let subCanvasX1 = Infinity; + let subCanvasX2 = 0; + let subCanvasY1 = Infinity; + let subCanvasY2 = 0; + const minX = Math.min(...parsedElements.map(element => element.x)); const minY = Math.min(...parsedElements.map(element => element.y)); - const dx = x - minX; - const dy = y - minY; + + const distance = (x: number, y: number) => { + return Math.abs(x > y ? x - y : y - x); + }; + + parsedElements.forEach(parsedElement => { + const [x1, y1, x2, y2] = getElementAbsoluteCoords(parsedElement); + subCanvasX1 = Math.min(subCanvasX1, x1); + subCanvasY1 = Math.min(subCanvasY1, y1); + subCanvasX2 = Math.max(subCanvasX2, x2); + subCanvasY2 = Math.max(subCanvasY2, y2); + }); + + const elementsCenterX = distance(subCanvasX1, subCanvasX2) / 2; + const elementsCenterY = distance(subCanvasY1, subCanvasY2) / 2; + + const dx = + this.state.cursorX - + this.state.scrollX - + CANVAS_WINDOW_OFFSET_LEFT - + elementsCenterX; + const dy = + this.state.cursorY - + this.state.scrollY - + CANVAS_WINDOW_OFFSET_TOP - + elementsCenterY; parsedElements.forEach(parsedElement => { const duplicate = duplicateElement(parsedElement); - duplicate.x += dx; - duplicate.y += dy; + duplicate.x += dx - minX; + duplicate.y += dy - minY; elements.push(duplicate); }); + this.forceUpdate(); } }; diff --git a/src/scene/comparisons.ts b/src/scene/comparisons.ts index 14e2ce9d..30418a93 100644 --- a/src/scene/comparisons.ts +++ b/src/scene/comparisons.ts @@ -22,9 +22,7 @@ export const hasStroke = (elements: ExcalidrawElement[]) => ); export const hasText = (elements: ExcalidrawElement[]) => - elements.some( - element => element.isSelected && element.type === "text" - ); + elements.some(element => element.isSelected && element.type === "text"); export function getElementAtPosition( elements: ExcalidrawElement[], diff --git a/src/types.ts b/src/types.ts index ea9af670..02a0c0e2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -11,5 +11,7 @@ export type AppState = { viewBackgroundColor: string; scrollX: number; scrollY: number; + cursorX: number; + cursorY: number; name: string; };