grid support (1st iteration) (#1788)

Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
Daishi Kato 2020-06-24 00:24:52 +09:00 committed by GitHub
parent b6bf011d0d
commit baa8fb6c14
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 343 additions and 80 deletions

View 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,
});
}
};

View file

@ -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";

View file

@ -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;
};