feat: resize text element (#1650)

* feat: resize text element

* ignore small font size change that leads jankiness

Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
Daishi Kato 2020-05-28 07:17:15 +09:00 committed by GitHub
parent 5327e8a3dc
commit 7edcea9a93
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 152 additions and 47 deletions

View file

@ -4,6 +4,7 @@ import { rescalePoints } from "../points";
import { rotate, adjustXYWithRotation, getFlipAdjustment } from "../math";
import {
ExcalidrawLinearElement,
ExcalidrawTextElement,
NonDeletedExcalidrawElement,
NonDeleted,
} from "./types";
@ -24,6 +25,7 @@ import {
getResizeCenterPointKey,
getResizeWithSidesSameLengthKey,
} from "../keys";
import { measureText, getFontString } from "../utils";
type ResizeTestType = ReturnType<typeof resizeTest>;
@ -55,6 +57,20 @@ export const resizeElements = (
pointerX,
pointerY,
);
} else if (
element.type === "text" &&
(resizeHandle === "nw" ||
resizeHandle === "ne" ||
resizeHandle === "sw" ||
resizeHandle === "se")
) {
resizeSingleTextElement(
element,
resizeHandle,
getResizeCenterPointKey(event),
pointerX,
pointerY,
);
} else if (resizeHandle) {
resizeSingleElement(
element,
@ -188,6 +204,95 @@ const rescalePointsInElement = (
}
: {};
const resizeSingleTextElement = (
element: NonDeleted<ExcalidrawTextElement>,
resizeHandle: "nw" | "ne" | "sw" | "se",
isResizeFromCenter: boolean,
pointerX: number,
pointerY: number,
) => {
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
const cx = (x1 + x2) / 2;
const cy = (y1 + y2) / 2;
// rotation pointer with reverse angle
const [rotatedX, rotatedY] = rotate(
pointerX,
pointerY,
cx,
cy,
-element.angle,
);
let scale;
switch (resizeHandle) {
case "se":
scale = Math.max(
(rotatedX - x1) / (x2 - x1),
(rotatedY - y1) / (y2 - y1),
);
break;
case "nw":
scale = Math.max(
(x2 - rotatedX) / (x2 - x1),
(y2 - rotatedY) / (y2 - y1),
);
break;
case "ne":
scale = Math.max(
(rotatedX - x1) / (x2 - x1),
(y2 - rotatedY) / (y2 - y1),
);
break;
case "sw":
scale = Math.max(
(x2 - rotatedX) / (x2 - x1),
(rotatedY - y1) / (y2 - y1),
);
break;
}
if (scale > 0) {
const newFontSize = Math.max(element.fontSize * scale, 10);
const metrics = measureText(
element.text,
getFontString({ fontSize: newFontSize, fontFamily: element.fontFamily }),
);
if (
Math.abs(metrics.width - element.width) <= 1 ||
Math.abs(metrics.height - element.height) <= 1
) {
// we ignore 1px change to avoid janky behavior
return;
}
const [nextX1, nextY1, nextX2, nextY2] = getResizedElementAbsoluteCoords(
element,
metrics.width,
metrics.height,
);
const deltaX1 = (x1 - nextX1) / 2;
const deltaY1 = (y1 - nextY1) / 2;
const deltaX2 = (x2 - nextX2) / 2;
const deltaY2 = (y2 - nextY2) / 2;
const [nextElementX, nextElementY] = adjustXYWithRotation(
resizeHandle,
element.x,
element.y,
element.angle,
deltaX1,
deltaY1,
deltaX2,
deltaY2,
isResizeFromCenter,
);
mutateElement(element, {
fontSize: newFontSize,
width: metrics.width,
height: metrics.height,
baseline: metrics.baseline,
x: nextElementX,
y: nextElementY,
});
}
};
const resizeSingleElement = (
element: NonDeletedExcalidrawElement,
resizeHandle: "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se",