Bind mode on precise binding

Fix binding to inside element

Fix initial arrow not following cursor (white dot)

Fix elbow arrow
This commit is contained in:
Mark Tolmacs
2025-07-18 19:11:09 +02:00
parent 1cfbc4b2ca
commit 892d2f425d
2 changed files with 47 additions and 52 deletions

View File

@ -139,7 +139,7 @@ export const bindOrUnbindBindingElement = (
); );
bindOrUnbindBindingElementEdge(arrow, start, "start", scene); bindOrUnbindBindingElementEdge(arrow, start, "start", scene);
bindOrUnbindBindingElementEdge(arrow, end, "end", scene); bindOrUnbindBindingElementEdge(arrow, end, "end", scene);
if (start.focusPoint || end.focusPoint) { if (!isElbowArrow(arrow) && (start.focusPoint || end.focusPoint)) {
// If the strategy dictates a focus point override, then // If the strategy dictates a focus point override, then
// update the arrow points to point to the focus point. // update the arrow points to point to the focus point.
const updates: PointsPositionUpdates = new Map(); const updates: PointsPositionUpdates = new Map();

View File

@ -6233,41 +6233,52 @@ class App extends React.Component<AppProps, AppState> {
multiElement.startBinding.mode === "orbit" multiElement.startBinding.mode === "orbit"
) { ) {
const elementsMap = this.scene.getNonDeletedElementsMap(); const elementsMap = this.scene.getNonDeletedElementsMap();
const startPoint = const hoveredElement = getHoveredElementForBinding(
LinearElementEditor.getPointAtIndexGlobalCoordinates( pointFrom<GlobalPoint>(scenePointerX, scenePointerY),
multiElement, this.scene.getNonDeletedElements(),
0, this.scene.getNonDeletedElementsMap(),
elementsMap, this.state.zoom,
);
const startElement = this.scene.getElement(
multiElement.startBinding.elementId,
) as ExcalidrawBindableElement;
const localPoint = updateBoundPoint(
multiElement,
"startBinding",
multiElement.startBinding,
startElement,
elementsMap,
); );
const avoidancePoint = localPoint if (
? LinearElementEditor.getPointGlobalCoordinates( !hoveredElement ||
hoveredElement.id !== multiElement.startBinding.elementId
) {
const startPoint =
LinearElementEditor.getPointAtIndexGlobalCoordinates(
multiElement, multiElement,
localPoint, 0,
elementsMap, elementsMap,
) );
: null; const startElement = this.scene.getElement(
if (avoidancePoint && !pointsEqual(startPoint, avoidancePoint)) { multiElement.startBinding.elementId,
const point = LinearElementEditor.pointFromAbsoluteCoords( ) as ExcalidrawBindableElement;
const localPoint = updateBoundPoint(
multiElement, multiElement,
avoidancePoint, "startBinding",
multiElement.startBinding,
startElement,
elementsMap, elementsMap,
); );
const avoidancePoint = localPoint
? LinearElementEditor.getPointGlobalCoordinates(
multiElement,
localPoint,
elementsMap,
)
: null;
if (avoidancePoint && !pointsEqual(startPoint, avoidancePoint)) {
const point = LinearElementEditor.pointFromAbsoluteCoords(
multiElement,
avoidancePoint,
elementsMap,
);
LinearElementEditor.movePoints( LinearElementEditor.movePoints(
multiElement, multiElement,
this.scene, this.scene,
new Map([[0, { point }]]), new Map([[0, { point }]]),
); );
}
} }
} }
@ -6933,9 +6944,13 @@ class App extends React.Component<AppProps, AppState> {
clearTimeout(this.bindModeHandler); clearTimeout(this.bindModeHandler);
} }
this.bindModeHandler = null; this.bindModeHandler = null;
this.setState({ // We need this iteration to complete binding and change
bindMode: "orbit", // back to orbit mode after that
}); setTimeout(() =>
this.setState({
bindMode: "orbit",
}),
);
} }
const scenePointer = viewportCoordsToSceneCoords( const scenePointer = viewportCoordsToSceneCoords(
@ -8494,26 +8509,6 @@ class App extends React.Component<AppProps, AppState> {
event[KEYS.CTRL_OR_CMD] ? null : this.getEffectiveGridSize(), event[KEYS.CTRL_OR_CMD] ? null : this.getEffectiveGridSize(),
); );
// for arrows/lines, don't start dragging until a given threshold
// to ensure we don't create a 2-point arrow by mistake when
// user clicks mouse in a way that it moves a tiny bit (thus
// triggering pointermove)
if (
!pointerDownState.drag.hasOccurred &&
(this.state.activeTool.type === "arrow" ||
this.state.activeTool.type === "line")
) {
if (
pointDistance(
pointFrom(pointerCoords.x, pointerCoords.y),
pointFrom(pointerDownState.origin.x, pointerDownState.origin.y),
) *
this.state.zoom.value <
MINIMUM_ARROW_SIZE
) {
return;
}
}
if (pointerDownState.resize.isResizing) { if (pointerDownState.resize.isResizing) {
pointerDownState.lastCoords.x = pointerCoords.x; pointerDownState.lastCoords.x = pointerCoords.x;
pointerDownState.lastCoords.y = pointerCoords.y; pointerDownState.lastCoords.y = pointerCoords.y;