feat: Try to preserve line angle on SHIFT+drag (#9570)

This commit is contained in:
Márk Tolmács
2025-05-27 12:39:45 +02:00
committed by GitHub
parent db2911c6c4
commit 864353be5f
5 changed files with 138 additions and 3 deletions

View File

@@ -149,6 +149,7 @@ export class LinearElementEditor {
public readonly hoverPointIndex: number;
public readonly segmentMidPointHoveredCoords: GlobalPoint | null;
public readonly elbowed: boolean;
public readonly customLineAngle: number | null;
constructor(
element: NonDeleted<ExcalidrawLinearElement>,
@@ -186,6 +187,7 @@ export class LinearElementEditor {
this.hoverPointIndex = -1;
this.segmentMidPointHoveredCoords = null;
this.elbowed = isElbowArrow(element) && element.elbowed;
this.customLineAngle = null;
}
// ---------------------------------------------------------------------------
@@ -289,6 +291,7 @@ export class LinearElementEditor {
const { elementId } = linearElementEditor;
const elementsMap = scene.getNonDeletedElementsMap();
const element = LinearElementEditor.getElement(elementId, elementsMap);
let customLineAngle = linearElementEditor.customLineAngle;
if (!element) {
return null;
}
@@ -329,6 +332,12 @@ export class LinearElementEditor {
const selectedIndex = selectedPointsIndices[0];
const referencePoint =
element.points[selectedIndex === 0 ? 1 : selectedIndex - 1];
customLineAngle =
linearElementEditor.customLineAngle ??
Math.atan2(
element.points[selectedIndex][1] - referencePoint[1],
element.points[selectedIndex][0] - referencePoint[0],
);
const [width, height] = LinearElementEditor._getShiftLockedDelta(
element,
@@ -336,6 +345,7 @@ export class LinearElementEditor {
referencePoint,
pointFrom(scenePointerX, scenePointerY),
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
customLineAngle,
);
LinearElementEditor.movePoints(
@@ -457,6 +467,7 @@ export class LinearElementEditor {
? lastClickedPoint
: -1,
isDragging: true,
customLineAngle,
};
}
@@ -574,6 +585,7 @@ export class LinearElementEditor {
: selectedPointsIndices,
isDragging: false,
pointerOffset: { x: 0, y: 0 },
customLineAngle: null,
};
}
@@ -1595,6 +1607,7 @@ export class LinearElementEditor {
referencePoint: LocalPoint,
scenePointer: GlobalPoint,
gridSize: NullableGridSize,
customLineAngle?: number,
) {
const referencePointCoords = LinearElementEditor.getPointGlobalCoordinates(
element,
@@ -1620,6 +1633,7 @@ export class LinearElementEditor {
referencePointCoords[1],
gridX,
gridY,
customLineAngle,
);
return pointRotateRads(

View File

@@ -2,6 +2,12 @@ import {
SHIFT_LOCKING_ANGLE,
viewportCoordsToSceneCoords,
} from "@excalidraw/common";
import {
normalizeRadians,
radiansBetweenAngles,
radiansDifference,
type Radians,
} from "@excalidraw/math";
import { pointsEqual } from "@excalidraw/math";
@@ -152,13 +158,42 @@ export const getLockedLinearCursorAlignSize = (
originY: number,
x: number,
y: number,
customAngle?: number,
) => {
let width = x - originX;
let height = y - originY;
const lockedAngle =
Math.round(Math.atan(height / width) / SHIFT_LOCKING_ANGLE) *
SHIFT_LOCKING_ANGLE;
const angle = Math.atan2(height, width) as Radians;
let lockedAngle = (Math.round(angle / SHIFT_LOCKING_ANGLE) *
SHIFT_LOCKING_ANGLE) as Radians;
if (customAngle) {
// If custom angle is provided, we check if the angle is close to the
// custom angle, snap to that if close engough, otherwise snap to the
// higher or lower angle depending on the current angle vs custom angle.
const lower = (Math.floor(customAngle / SHIFT_LOCKING_ANGLE) *
SHIFT_LOCKING_ANGLE) as Radians;
if (
radiansBetweenAngles(
angle,
lower,
(lower + SHIFT_LOCKING_ANGLE) as Radians,
)
) {
if (
radiansDifference(angle, customAngle as Radians) <
SHIFT_LOCKING_ANGLE / 6
) {
lockedAngle = customAngle as Radians;
} else if (
normalizeRadians(angle) > normalizeRadians(customAngle as Radians)
) {
lockedAngle = (lower + SHIFT_LOCKING_ANGLE) as Radians;
} else {
lockedAngle = lower;
}
}
}
if (lockedAngle === 0) {
height = 0;