Update simple arrow fixed point when arrow is dragged or moved by arrow keys

This commit is contained in:
Mark Tolmacs
2025-06-26 18:33:13 +02:00
parent 8a2d3f7874
commit 5a3c1469d1
2 changed files with 93 additions and 2 deletions

View File

@ -1400,7 +1400,7 @@ export const snapToMid = (
return p;
};
const updateBoundPoint = (
export const updateBoundPoint = (
linearElement: NonDeleted<ExcalidrawLinearElement>,
startOrEnd: "startBinding" | "endBinding",
binding: PointBinding | null | undefined,

View File

@ -235,6 +235,8 @@ import {
isLineElement,
isSimpleArrow,
getOutlineAvoidingPoint,
isFixedPointBinding,
calculateFixedPointForNonElbowArrowBinding,
} from "@excalidraw/element";
import type { GlobalPoint, LocalPoint, Radians } from "@excalidraw/math";
@ -261,6 +263,7 @@ import type {
MagicGenerationData,
ExcalidrawArrowElement,
ExcalidrawElbowArrowElement,
ExcalidrawBindableElement,
} from "@excalidraw/element/types";
import type { Mutable, ValueOf } from "@excalidraw/common/utility-types";
@ -4629,6 +4632,51 @@ class App extends React.Component<AppProps, AppState> {
this.scene,
this.state.zoom,
);
const elementsMap = this.scene.getNonDeletedElementsMap();
this.scene
.getSelectedElements(this.state)
.filter(isSimpleArrow)
.forEach((element) => {
// Update the fixed point bindings for non-elbow arrows
// when the pointer is released, so that they are correctly positioned
// after the drag.
if (
element.startBinding &&
isFixedPointBinding(element.startBinding)
) {
this.scene.mutateElement(element, {
startBinding: {
...element.startBinding,
...calculateFixedPointForNonElbowArrowBinding(
element,
elementsMap.get(
element.startBinding.elementId,
) as ExcalidrawBindableElement,
"start",
elementsMap,
),
},
});
}
if (element.endBinding && isFixedPointBinding(element.endBinding)) {
this.scene.mutateElement(element, {
endBinding: {
...element.endBinding,
...calculateFixedPointForNonElbowArrowBinding(
element,
elementsMap.get(
element.endBinding.elementId,
) as ExcalidrawBindableElement,
"end",
elementsMap,
),
},
});
}
});
this.setState({ suggestedBindings: [] });
}
@ -9077,6 +9125,8 @@ class App extends React.Component<AppProps, AppState> {
pointerDownState: PointerDownState,
): (event: PointerEvent) => void {
return withBatchedUpdates((childEvent: PointerEvent) => {
const elementsMap = this.scene.getNonDeletedElementsMap();
this.removePointer(childEvent);
if (pointerDownState.eventListeners.onMove) {
pointerDownState.eventListeners.onMove.flush();
@ -9168,7 +9218,6 @@ class App extends React.Component<AppProps, AppState> {
selectedElementsAreBeingDragged: false,
bindMode: "focus",
});
const elementsMap = this.scene.getNonDeletedElementsMap();
if (
pointerDownState.drag.hasOccurred &&
@ -9242,6 +9291,48 @@ class App extends React.Component<AppProps, AppState> {
}
}
this.scene
.getSelectedElements(this.state)
.filter(isSimpleArrow)
.forEach((element) => {
// Update the fixed point bindings for non-elbow arrows
// when the pointer is released, so that they are correctly positioned
// after the drag.
if (
element.startBinding &&
isFixedPointBinding(element.startBinding)
) {
this.scene.mutateElement(element, {
startBinding: {
...element.startBinding,
...calculateFixedPointForNonElbowArrowBinding(
element,
elementsMap.get(
element.startBinding.elementId,
) as ExcalidrawBindableElement,
"start",
elementsMap,
),
},
});
}
if (element.endBinding && isFixedPointBinding(element.endBinding)) {
this.scene.mutateElement(element, {
endBinding: {
...element.endBinding,
...calculateFixedPointForNonElbowArrowBinding(
element,
elementsMap.get(
element.endBinding.elementId,
) as ExcalidrawBindableElement,
"end",
elementsMap,
),
},
});
}
});
this.missingPointerEventCleanupEmitter.clear();
window.removeEventListener(