diff --git a/packages/element/src/binding.ts b/packages/element/src/binding.ts index 0b39ddad58..9197faf60b 100644 --- a/packages/element/src/binding.ts +++ b/packages/element/src/binding.ts @@ -146,6 +146,8 @@ export const isBindingEnabled = (appState: AppState): boolean => { export const bindOrUnbindBindingElement = ( arrow: NonDeleted, draggingPoints: PointsPositionUpdates, + scenePointerX: number, + scenePointerY: number, scene: Scene, appState: AppState, opts?: { @@ -158,6 +160,8 @@ export const bindOrUnbindBindingElement = ( const { start, end } = getBindingStrategyForDraggingBindingElementEndpoints( arrow, draggingPoints, + scenePointerX, + scenePointerY, scene.getNonDeletedElementsMap(), scene.getNonDeletedElements(), appState, @@ -557,6 +561,8 @@ const bindingStrategyForSimpleArrowEndpointDragging_complex = ( export const getBindingStrategyForDraggingBindingElementEndpoints = ( arrow: NonDeleted, draggingPoints: PointsPositionUpdates, + screenPointerX: number, + screenPointerY: number, elementsMap: NonDeletedSceneElementsMap, elements: readonly Ordered[], appState: AppState, @@ -583,6 +589,8 @@ export const getBindingStrategyForDraggingBindingElementEndpoints = ( return getBindingStrategyForDraggingBindingElementEndpoints_simple( arrow, draggingPoints, + screenPointerX, + screenPointerY, elementsMap, elements, appState, @@ -593,6 +601,8 @@ export const getBindingStrategyForDraggingBindingElementEndpoints = ( const getBindingStrategyForDraggingBindingElementEndpoints_simple = ( arrow: NonDeleted, draggingPoints: PointsPositionUpdates, + scenePointerX: number, + scenePointerY: number, elementsMap: NonDeletedSceneElementsMap, elements: readonly Ordered[], appState: AppState, @@ -670,7 +680,15 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = ( elementsMap, (e) => maxBindingDistance_simple(appState.zoom), ); - const pointInElement = hit && isPointInElement(globalPoint, hit, elementsMap); + const pointInElement = + hit && + (opts?.angleLocked + ? isPointInElement( + pointFrom(scenePointerX, scenePointerY), + hit, + elementsMap, + ) + : isPointInElement(globalPoint, hit, elementsMap)); const otherBindableElement = otherBinding ? (elementsMap.get( otherBinding.elementId, @@ -944,6 +962,8 @@ export const bindOrUnbindBindingElements = ( bindOrUnbindBindingElement( arrow, new Map(), // No dragging points in this case + Infinity, + Infinity, scene, appState, ); @@ -1146,7 +1166,14 @@ export const updateBindings = ( }, ) => { if (isArrowElement(latestElement)) { - bindOrUnbindBindingElement(latestElement, new Map(), scene, appState); + bindOrUnbindBindingElement( + latestElement, + new Map(), + Infinity, + Infinity, + scene, + appState, + ); } else { updateBoundElements(latestElement, scene, { ...options, diff --git a/packages/element/src/linearElementEditor.ts b/packages/element/src/linearElementEditor.ts index 7dd834a621..b8c82c0875 100644 --- a/packages/element/src/linearElementEditor.ts +++ b/packages/element/src/linearElementEditor.ts @@ -343,6 +343,8 @@ export class LinearElementEditor { [idx], deltaX, deltaY, + scenePointerX, + scenePointerY, elementsMap, element, elements, @@ -498,7 +500,6 @@ export class LinearElementEditor { width + pivotPoint[0], height + pivotPoint[1], ); - deltaX = target[0] - draggingPoint[0]; deltaY = target[1] - draggingPoint[1]; } else { @@ -519,6 +520,8 @@ export class LinearElementEditor { selectedPointsIndices, deltaX, deltaY, + scenePointerX, + scenePointerY, elementsMap, element, elements, @@ -2066,6 +2069,8 @@ const pointDraggingUpdates = ( selectedPointsIndices: readonly number[], deltaX: number, deltaY: number, + scenePointerX: number, + scenePointerY: number, elementsMap: NonDeletedSceneElementsMap, element: NonDeleted, elements: readonly Ordered[], @@ -2106,6 +2111,8 @@ const pointDraggingUpdates = ( const { start, end } = getBindingStrategyForDraggingBindingElementEndpoints( element, naiveDraggingPoints, + scenePointerX, + scenePointerY, elementsMap, elements, app.state, @@ -2228,10 +2235,15 @@ const pointDraggingUpdates = ( // We need to use a custom intersector to ensure that if there is a big "jump" // in the arrow's position, we can position it with outline avoidance // pixel-perfectly and avoid "dancing" arrows. - const customIntersector = + // NOTE: Direction matters here, so we create two intersectors + const startCustomIntersector = start.focusPoint && end.focusPoint ? lineSegment(start.focusPoint, end.focusPoint) : undefined; + const endCustomIntersector = + start.focusPoint && end.focusPoint + ? lineSegment(end.focusPoint, start.focusPoint) + : undefined; // Needed to handle a special case where an existing arrow is dragged over // the same element it is bound to on the other side @@ -2268,7 +2280,7 @@ const pointDraggingUpdates = ( nextArrow.endBinding, endBindable, elementsMap, - customIntersector, + endCustomIntersector, ) || nextArrow.points[nextArrow.points.length - 1] : nextArrow.points[nextArrow.points.length - 1]; @@ -2299,7 +2311,7 @@ const pointDraggingUpdates = ( nextArrow.startBinding, startBindable, elementsMap, - customIntersector, + startCustomIntersector, ) || nextArrow.points[0] : nextArrow.points[0]; diff --git a/packages/excalidraw/actions/actionFinalize.tsx b/packages/excalidraw/actions/actionFinalize.tsx index c6d9287f6c..04cb97ff77 100644 --- a/packages/excalidraw/actions/actionFinalize.tsx +++ b/packages/excalidraw/actions/actionFinalize.tsx @@ -103,11 +103,19 @@ export const actionFinalize = register({ return map; }, new Map()) ?? new Map(); - bindOrUnbindBindingElement(element, draggedPoints, scene, appState, { - newArrow, - altKey: event.altKey, - angleLocked: shouldRotateWithDiscreteAngle(event), - }); + bindOrUnbindBindingElement( + element, + draggedPoints, + sceneCoords.x, + sceneCoords.y, + scene, + appState, + { + newArrow, + altKey: event.altKey, + angleLocked: shouldRotateWithDiscreteAngle(event), + }, + ); } else if (isLineElement(element)) { if ( appState.selectedLinearElement?.isEditing && diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index f4190fe8fa..b5176e3d46 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -8617,6 +8617,8 @@ class App extends React.Component { }, ], ]), + point[0], + point[1], this.scene, this.state, {