resize elements from center point (#1225)

* add hint & support multi-line hints

* resize from center point using the new resize maths

* resize with origin element when lifting alt key

* add readonly to elementOriginPosition

* add setResizeWithCenterKeyLifted

* isResizeFromCenter logic

* offsetX and offsetY

* simplify equations

* creating element from center point

* lint

* lint

* lint

* remove revert on key up logic

Co-authored-by: dwelle <luzar.david@gmail.com>
Co-authored-by: daishi <daishi@axlight.com>
This commit is contained in:
José Quinto 2020-04-22 16:57:17 +01:00 committed by GitHub
parent 8c49770e3b
commit ed6fb60337
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 69 additions and 22 deletions

View File

@ -71,7 +71,12 @@ import {
sceneCoordsToViewportCoords, sceneCoordsToViewportCoords,
setCursorForShape, setCursorForShape,
} from "../utils"; } from "../utils";
import { KEYS, isArrowKey } from "../keys"; import {
KEYS,
isArrowKey,
getResizeCenterPointKey,
getResizeWithSidesSameLengthKey,
} from "../keys";
import { findShapeByKey, shapesShortcutKeys } from "../shapes"; import { findShapeByKey, shapesShortcutKeys } from "../shapes";
import { createHistory, SceneHistory } from "../history"; import { createHistory, SceneHistory } from "../history";
@ -1801,6 +1806,7 @@ class App extends React.Component<any, AppState> {
let draggingOccurred = false; let draggingOccurred = false;
let hitElement: ExcalidrawElement | null = null; let hitElement: ExcalidrawElement | null = null;
let hitElementWasAddedToSelection = false; let hitElementWasAddedToSelection = false;
if (this.state.elementType === "selection") { if (this.state.elementType === "selection") {
const elements = globalSceneState.getElements(); const elements = globalSceneState.getElements();
const selectedElements = getSelectedElements(elements, this.state); const selectedElements = getSelectedElements(elements, this.state);
@ -2021,7 +2027,7 @@ class App extends React.Component<any, AppState> {
} }
let resizeArrowFn: ResizeArrowFnType | null = null; let resizeArrowFn: ResizeArrowFnType | null = null;
const setResizeArrrowFn = (fn: ResizeArrowFnType) => { const setResizeArrowFn = (fn: ResizeArrowFnType) => {
resizeArrowFn = fn; resizeArrowFn = fn;
}; };
@ -2082,7 +2088,7 @@ class App extends React.Component<any, AppState> {
this.state, this.state,
this.setAppState, this.setAppState,
resizeArrowFn, resizeArrowFn,
setResizeArrrowFn, setResizeArrowFn,
event, event,
x, x,
y, y,
@ -2189,7 +2195,7 @@ class App extends React.Component<any, AppState> {
}); });
} }
} else { } else {
if (event.shiftKey) { if (getResizeWithSidesSameLengthKey(event)) {
({ width, height } = getPerfectElementSize( ({ width, height } = getPerfectElementSize(
this.state.elementType, this.state.elementType,
width, width,
@ -2201,9 +2207,19 @@ class App extends React.Component<any, AppState> {
} }
} }
let newX = x < originX ? originX - width : originX;
let newY = y < originY ? originY - height : originY;
if (getResizeCenterPointKey(event)) {
width += width;
height += height;
newX = originX - width / 2;
newY = originY - height / 2;
}
mutateElement(draggingElement, { mutateElement(draggingElement, {
x: x < originX ? originX - width : originX, x: newX,
y: y < originY ? originY - height : originY, y: newY,
width: width, width: width,
height: height, height: height,
}); });

View File

@ -8,11 +8,12 @@
position: absolute; position: absolute;
top: 54px; top: 54px;
transform: translateX(calc(-50% - 16px)); /* 16px is half of lock icon */ transform: translateX(calc(-50% - 16px)); /* 16px is half of lock icon */
white-space: pre;
text-align: center;
@media #{$media-query} { @media #{$media-query} {
position: static; position: static;
transform: none; transform: none;
margin-top: 0.5rem; margin-top: 0.5rem;
text-align: center;
} }
> span { > span {

View File

@ -19,6 +19,10 @@ import {
getCursorForResizingElement, getCursorForResizingElement,
normalizeResizeHandle, normalizeResizeHandle,
} from "./resizeTest"; } from "./resizeTest";
import {
getResizeCenterPointKey,
getResizeWithSidesSameLengthKey,
} from "../keys";
type ResizeTestType = ReturnType<typeof resizeTest>; type ResizeTestType = ReturnType<typeof resizeTest>;
@ -117,13 +121,13 @@ export const resizeElements = (
setResizeHandle: (nextResizeHandle: ResizeTestType) => void, setResizeHandle: (nextResizeHandle: ResizeTestType) => void,
appState: AppState, appState: AppState,
setAppState: (obj: any) => void, setAppState: (obj: any) => void,
resizeArrowFn: ResizeArrowFnType | null, resizeArrowFn: ResizeArrowFnType | null, // XXX eliminate in #1339
setResizeArrowFn: (fn: ResizeArrowFnType) => void, setResizeArrowFn: (fn: ResizeArrowFnType) => void, // XXX eliminate in #1339
event: PointerEvent, event: PointerEvent, // XXX we want to make it independent?
xPointer: number, xPointer: number,
yPointer: number, yPointer: number,
lastX: number, lastX: number, // XXX eliminate in #1339
lastY: number, lastY: number, // XXX eliminate in #1339
) => { ) => {
setAppState({ setAppState({
isResizing: resizeHandle !== "rotation", isResizing: resizeHandle !== "rotation",
@ -191,7 +195,8 @@ export const resizeElements = (
xPointer, xPointer,
yPointer, yPointer,
offsetPointer, offsetPointer,
event.shiftKey, getResizeWithSidesSameLengthKey(event),
getResizeCenterPointKey(event),
); );
if (resized.width !== 0 && resized.height !== 0) { if (resized.width !== 0 && resized.height !== 0) {
mutateElement(element, { mutateElement(element, {

View File

@ -14,6 +14,7 @@ export const KEYS = {
SPACE: " ", SPACE: " ",
QUESTION_MARK: "?", QUESTION_MARK: "?",
F_KEY_CODE: 70, F_KEY_CODE: 70,
ALT_KEY_CODE: 18,
} as const; } as const;
export type Key = keyof typeof KEYS; export type Key = keyof typeof KEYS;
@ -26,3 +27,8 @@ export function isArrowKey(keyCode: string) {
keyCode === KEYS.ARROW_UP keyCode === KEYS.ARROW_UP
); );
} }
export const getResizeCenterPointKey = (event: MouseEvent | KeyboardEvent) =>
event.altKey || event.which === KEYS.ALT_KEY_CODE;
export const getResizeWithSidesSameLengthKey = (event: MouseEvent) =>
event.shiftKey;

View File

@ -109,7 +109,7 @@
"hints": { "hints": {
"linearElement": "Click to start multiple points, drag for single line", "linearElement": "Click to start multiple points, drag for single line",
"linearElementMulti": "Click on last point or press Escape or Enter to finish", "linearElementMulti": "Click on last point or press Escape or Enter to finish",
"resize": "You can constrain proportions by holding SHIFT while resizing", "resize": "You can constrain proportions by holding SHIFT while resizing,\nhold ALT to resize from the center",
"rotate": "You can constrain angles by holding SHIFT while rotating" "rotate": "You can constrain angles by holding SHIFT while rotating"
}, },
"errorSplash": { "errorSplash": {

View File

@ -63,26 +63,43 @@ const adjustXYWithRotation = (
angle: number, angle: number,
deltaX: number, deltaX: number,
deltaY: number, deltaY: number,
isResizeFromCenter: boolean,
) => { ) => {
const cos = Math.cos(angle); const cos = Math.cos(angle);
const sin = Math.sin(angle); const sin = Math.sin(angle);
deltaX /= 2; deltaX /= 2;
deltaY /= 2; deltaY /= 2;
if (side === "e" || side === "ne" || side === "se") { if (side === "e" || side === "ne" || side === "se") {
x += deltaX * (1 - cos); if (isResizeFromCenter) {
y += deltaX * -sin; x += deltaX;
} else {
x += deltaX * (1 - cos);
y += deltaX * -sin;
}
} }
if (side === "s" || side === "sw" || side === "se") { if (side === "s" || side === "sw" || side === "se") {
x += deltaY * sin; if (isResizeFromCenter) {
y += deltaY * (1 - cos); y += deltaY;
} else {
x += deltaY * sin;
y += deltaY * (1 - cos);
}
} }
if (side === "w" || side === "nw" || side === "sw") { if (side === "w" || side === "nw" || side === "sw") {
x += deltaX * (1 + cos); if (isResizeFromCenter) {
y += deltaX * sin; x += deltaX;
} else {
x += deltaX * (1 + cos);
y += deltaX * sin;
}
} }
if (side === "n" || side === "nw" || side === "ne") { if (side === "n" || side === "nw" || side === "ne") {
x += deltaY * -sin; if (isResizeFromCenter) {
y += deltaY * (1 + cos); y += deltaY;
} else {
x += deltaY * -sin;
y += deltaY * (1 + cos);
}
} }
return { x, y }; return { x, y };
}; };
@ -100,6 +117,7 @@ export const resizeXYWidthHightWithRotation = (
yPointer: number, yPointer: number,
offsetPointer: number, offsetPointer: number,
sidesWithSameLength: boolean, sidesWithSameLength: boolean,
isResizeFromCenter: boolean,
) => { ) => {
// center point for rotation // center point for rotation
const cx = x + width / 2; const cx = x + width / 2;
@ -139,6 +157,7 @@ export const resizeXYWidthHightWithRotation = (
angle, angle,
width - nextWidth, width - nextWidth,
height - nextHeight, height - nextHeight,
isResizeFromCenter,
), ),
}; };
}; };