Multipoint arrows now have single point commit in binding zones

This commit is contained in:
Mark Tolmacs 2025-03-04 20:08:44 +01:00
parent ad8220c529
commit 11fe608f9a
3 changed files with 92 additions and 25 deletions

View file

@ -238,24 +238,23 @@ export class LinearElementEditor {
});
}
static getOutlineAvoidingPoint(
static getOutlineAvoidingPointOrNull(
element: NonDeleted<ExcalidrawLinearElement>,
coords: { x: number; y: number },
pointIndex: number,
app: AppClassProperties,
): GlobalPoint {
const elbowed = isElbowArrow(element);
) {
const hoveredElement = getHoveredElementForBinding(
coords,
app.scene.getNonDeletedElements(),
app.scene.getNonDeletedElementsMap(),
app.state.zoom,
true,
elbowed,
isElbowArrow(element),
);
const p = pointFrom<GlobalPoint>(coords.x, coords.y);
if (hoveredElement) {
const p = pointFrom<GlobalPoint>(coords.x, coords.y);
const newPoints = Array.from(element.points);
newPoints[pointIndex] = pointFrom<LocalPoint>(
p[0] - element.x,
@ -273,7 +272,27 @@ export class LinearElementEditor {
);
}
return p;
return null;
}
static getOutlineAvoidingPoint(
element: NonDeleted<ExcalidrawLinearElement>,
coords: { x: number; y: number },
pointIndex: number,
app: AppClassProperties,
): GlobalPoint {
const p = LinearElementEditor.getOutlineAvoidingPointOrNull(
element,
coords,
pointIndex,
app,
);
if (p) {
return p;
}
return pointFrom<GlobalPoint>(coords.x, coords.y);
}
/**

View file

@ -91,10 +91,26 @@ export const actionFinalize = register({
multiPointElement.type !== "freedraw" &&
appState.lastPointerDownWith !== "touch"
) {
const { points, lastCommittedPoint } = multiPointElement;
const { x: rx, y: ry, points, lastCommittedPoint } = multiPointElement;
const lastGlobalPoint = pointFrom<GlobalPoint>(
rx + points[points.length - 1][0],
ry + points[points.length - 1][1],
);
const hoveredElementForBinding = getHoveredElementForBinding(
{
x: lastGlobalPoint[0],
y: lastGlobalPoint[1],
},
elements,
elementsMap,
app.state.zoom,
true,
isElbowArrow(multiPointElement),
);
if (
!lastCommittedPoint ||
points[points.length - 1] !== lastCommittedPoint
!hoveredElementForBinding &&
(!lastCommittedPoint ||
points[points.length - 1] !== lastCommittedPoint)
) {
mutateElement(multiPointElement, {
points: multiPointElement.points.slice(0, -1),

View file

@ -5965,17 +5965,33 @@ class App extends React.Component<AppProps, AppState> {
if (isPathALoop(points, this.state.zoom.value)) {
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
}
const outlineGlobalPoint =
LinearElementEditor.getOutlineAvoidingPointOrNull(
multiElement,
{
x: scenePointerX,
y: scenePointerY,
},
multiElement.points.length - 1,
this,
);
const nextPoint = outlineGlobalPoint
? pointFrom<LocalPoint>(
outlineGlobalPoint[0] - rx,
outlineGlobalPoint[1] - ry,
)
: pointFrom<LocalPoint>(
lastCommittedX + dxFromLastCommitted,
lastCommittedY + dyFromLastCommitted,
);
// update last uncommitted point
mutateElement(
multiElement,
{
points: [
...points.slice(0, -1),
pointFrom<LocalPoint>(
lastCommittedX + dxFromLastCommitted,
lastCommittedY + dyFromLastCommitted,
),
],
points: [...points.slice(0, -1), nextPoint],
},
false,
{
@ -7715,18 +7731,34 @@ class App extends React.Component<AppProps, AppState> {
}
const { x: rx, y: ry, lastCommittedPoint } = multiElement;
const lastGlobalPoint = pointFrom<GlobalPoint>(
rx + multiElement.points[multiElement.points.length - 1][0],
ry + multiElement.points[multiElement.points.length - 1][1],
);
const hoveredElementForBinding = getHoveredElementForBinding(
{
x: lastGlobalPoint[0],
y: lastGlobalPoint[1],
},
this.scene.getNonDeletedElements(),
this.scene.getNonDeletedElementsMap(),
this.state.zoom,
true,
isElbowArrow(multiElement),
);
// clicking inside commit zone → finalize arrow
if (
multiElement.points.length > 1 &&
lastCommittedPoint &&
pointDistance(
pointFrom(
pointerDownState.origin.x - rx,
pointerDownState.origin.y - ry,
),
lastCommittedPoint,
) < LINE_CONFIRM_THRESHOLD
!!hoveredElementForBinding ||
(multiElement.points.length > 1 &&
lastCommittedPoint &&
pointDistance(
pointFrom(
pointerDownState.origin.x - rx,
pointerDownState.origin.y - ry,
),
lastCommittedPoint,
) < LINE_CONFIRM_THRESHOLD)
) {
this.actionManager.executeAction(actionFinalize);
return;