mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
grid support (1st iteration) (#1788)
Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
parent
b6bf011d0d
commit
baa8fb6c14
14 changed files with 343 additions and 80 deletions
72
src/element/dragElements.ts
Normal file
72
src/element/dragElements.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
import { NonDeletedExcalidrawElement } from "./types";
|
||||
import { getCommonBounds } from "./bounds";
|
||||
import { mutateElement } from "./mutateElement";
|
||||
import { SHAPES } from "../shapes";
|
||||
import { getPerfectElementSize } from "./sizeHelpers";
|
||||
|
||||
export const dragSelectedElements = (
|
||||
selectedElements: NonDeletedExcalidrawElement[],
|
||||
pointerX: number,
|
||||
pointerY: number,
|
||||
) => {
|
||||
const [x1, y1] = getCommonBounds(selectedElements);
|
||||
selectedElements.forEach((element) => {
|
||||
mutateElement(element, {
|
||||
x: pointerX + element.x - x1,
|
||||
y: pointerY + element.y - y1,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const getDragOffsetXY = (
|
||||
selectedElements: NonDeletedExcalidrawElement[],
|
||||
x: number,
|
||||
y: number,
|
||||
): [number, number] => {
|
||||
const [x1, y1] = getCommonBounds(selectedElements);
|
||||
return [x - x1, y - y1];
|
||||
};
|
||||
|
||||
export const dragNewElement = (
|
||||
draggingElement: NonDeletedExcalidrawElement,
|
||||
elementType: typeof SHAPES[number]["value"],
|
||||
originX: number,
|
||||
originY: number,
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
isResizeWithSidesSameLength: boolean,
|
||||
isResizeCenterPoint: boolean,
|
||||
) => {
|
||||
if (isResizeWithSidesSameLength) {
|
||||
({ width, height } = getPerfectElementSize(
|
||||
elementType,
|
||||
width,
|
||||
y < originY ? -height : height,
|
||||
));
|
||||
|
||||
if (height < 0) {
|
||||
height = -height;
|
||||
}
|
||||
}
|
||||
|
||||
let newX = x < originX ? originX - width : originX;
|
||||
let newY = y < originY ? originY - height : originY;
|
||||
|
||||
if (isResizeCenterPoint) {
|
||||
width += width;
|
||||
height += height;
|
||||
newX = originX - width / 2;
|
||||
newY = originY - height / 2;
|
||||
}
|
||||
|
||||
if (width !== 0 && height !== 0) {
|
||||
mutateElement(draggingElement, {
|
||||
x: newX,
|
||||
y: newY,
|
||||
width: width,
|
||||
height: height,
|
||||
});
|
||||
}
|
||||
};
|
|
@ -38,6 +38,11 @@ export {
|
|||
getResizeOffsetXY,
|
||||
getResizeArrowDirection,
|
||||
} from "./resizeElements";
|
||||
export {
|
||||
dragSelectedElements,
|
||||
getDragOffsetXY,
|
||||
dragNewElement,
|
||||
} from "./dragElements";
|
||||
export { isTextElement, isExcalidrawElement } from "./typeChecks";
|
||||
export { textWysiwyg } from "./textWysiwyg";
|
||||
export { redrawTextBoundingBox } from "./textElement";
|
||||
|
|
|
@ -21,10 +21,6 @@ import {
|
|||
getCursorForResizingElement,
|
||||
normalizeResizeHandle,
|
||||
} from "./resizeTest";
|
||||
import {
|
||||
getResizeCenterPointKey,
|
||||
getResizeWithSidesSameLengthKey,
|
||||
} from "../keys";
|
||||
import { measureText, getFontString } from "../utils";
|
||||
|
||||
type ResizeTestType = ReturnType<typeof resizeTest>;
|
||||
|
@ -34,14 +30,21 @@ export const resizeElements = (
|
|||
setResizeHandle: (nextResizeHandle: ResizeTestType) => void,
|
||||
selectedElements: NonDeletedExcalidrawElement[],
|
||||
resizeArrowDirection: "origin" | "end",
|
||||
event: PointerEvent, // XXX we want to make it independent?
|
||||
isRotateWithDiscreteAngle: boolean,
|
||||
isResizeWithSidesSameLength: boolean,
|
||||
isResizeCenterPoint: boolean,
|
||||
pointerX: number,
|
||||
pointerY: number,
|
||||
) => {
|
||||
if (selectedElements.length === 1) {
|
||||
const [element] = selectedElements;
|
||||
if (resizeHandle === "rotation") {
|
||||
rotateSingleElement(element, pointerX, pointerY, event.shiftKey);
|
||||
rotateSingleElement(
|
||||
element,
|
||||
pointerX,
|
||||
pointerY,
|
||||
isRotateWithDiscreteAngle,
|
||||
);
|
||||
} else if (
|
||||
isLinearElement(element) &&
|
||||
element.points.length === 2 &&
|
||||
|
@ -53,7 +56,7 @@ export const resizeElements = (
|
|||
resizeSingleTwoPointElement(
|
||||
element,
|
||||
resizeArrowDirection,
|
||||
event.shiftKey,
|
||||
isRotateWithDiscreteAngle,
|
||||
pointerX,
|
||||
pointerY,
|
||||
);
|
||||
|
@ -67,7 +70,7 @@ export const resizeElements = (
|
|||
resizeSingleTextElement(
|
||||
element,
|
||||
resizeHandle,
|
||||
getResizeCenterPointKey(event),
|
||||
isResizeCenterPoint,
|
||||
pointerX,
|
||||
pointerY,
|
||||
);
|
||||
|
@ -75,8 +78,8 @@ export const resizeElements = (
|
|||
resizeSingleElement(
|
||||
element,
|
||||
resizeHandle,
|
||||
getResizeWithSidesSameLengthKey(event),
|
||||
getResizeCenterPointKey(event),
|
||||
isResizeWithSidesSameLength,
|
||||
isResizeCenterPoint,
|
||||
pointerX,
|
||||
pointerY,
|
||||
);
|
||||
|
@ -114,13 +117,13 @@ const rotateSingleElement = (
|
|||
element: NonDeletedExcalidrawElement,
|
||||
pointerX: number,
|
||||
pointerY: number,
|
||||
isAngleLocking: boolean,
|
||||
isRotateWithDiscreteAngle: boolean,
|
||||
) => {
|
||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
|
||||
const cx = (x1 + x2) / 2;
|
||||
const cy = (y1 + y2) / 2;
|
||||
let angle = (5 * Math.PI) / 2 + Math.atan2(pointerY - cy, pointerX - cx);
|
||||
if (isAngleLocking) {
|
||||
if (isRotateWithDiscreteAngle) {
|
||||
angle += SHIFT_LOCKING_ANGLE / 2;
|
||||
angle -= angle % SHIFT_LOCKING_ANGLE;
|
||||
}
|
||||
|
@ -133,14 +136,14 @@ const rotateSingleElement = (
|
|||
const resizeSingleTwoPointElement = (
|
||||
element: NonDeleted<ExcalidrawLinearElement>,
|
||||
resizeArrowDirection: "origin" | "end",
|
||||
isAngleLocking: boolean,
|
||||
isRotateWithDiscreteAngle: boolean,
|
||||
pointerX: number,
|
||||
pointerY: number,
|
||||
) => {
|
||||
const pointOrigin = element.points[0]; // can assume always [0, 0]?
|
||||
const pointEnd = element.points[1];
|
||||
if (resizeArrowDirection === "end") {
|
||||
if (isAngleLocking) {
|
||||
if (isRotateWithDiscreteAngle) {
|
||||
const { width, height } = getPerfectElementSize(
|
||||
element.type,
|
||||
pointerX - element.x,
|
||||
|
@ -162,7 +165,7 @@ const resizeSingleTwoPointElement = (
|
|||
}
|
||||
} else {
|
||||
// resizeArrowDirection === "origin"
|
||||
if (isAngleLocking) {
|
||||
if (isRotateWithDiscreteAngle) {
|
||||
const { width, height } = getPerfectElementSize(
|
||||
element.type,
|
||||
element.x + pointEnd[0] - pointOrigin[0] - pointerX,
|
||||
|
@ -232,6 +235,16 @@ const measureFontSizeFromWH = (
|
|||
if (metrics.width - nextWidth < 1 && metrics.height - nextHeight < 1) {
|
||||
return { size: nextFontSize, baseline: metrics.baseline };
|
||||
}
|
||||
// third measurement
|
||||
scale *= 0.99; // just heuristics
|
||||
nextFontSize = element.fontSize * scale;
|
||||
metrics = measureText(
|
||||
element.text,
|
||||
getFontString({ fontSize: nextFontSize, fontFamily: element.fontFamily }),
|
||||
);
|
||||
if (metrics.width - nextWidth < 1 && metrics.height - nextHeight < 1) {
|
||||
return { size: nextFontSize, baseline: metrics.baseline };
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue