diff --git a/packages/element/src/arrows/focus.ts b/packages/element/src/arrows/focus.ts index 643ec48ac5..8202777a26 100644 --- a/packages/element/src/arrows/focus.ts +++ b/packages/element/src/arrows/focus.ts @@ -10,7 +10,6 @@ import { getBindingGap, getGlobalFixedPointForBindableElement, isBindingEnabled, - maxBindingDistance_simple, unbindBindingElement, updateBoundPoint, } from "../binding"; @@ -239,7 +238,7 @@ export const handleFocusPointDrag = ( point, scene.getNonDeletedElements(), elementsMap, - maxBindingDistance_simple(appState.zoom), + appState.zoom, ); // Hovering a bindable element diff --git a/packages/element/src/binding.ts b/packages/element/src/binding.ts index bf3044b95d..a1dd1bf8ec 100644 --- a/packages/element/src/binding.ts +++ b/packages/element/src/binding.ts @@ -274,7 +274,7 @@ const bindingStrategyForElbowArrowEndpointDragging = ( globalPoint, elements, elementsMap, - maxBindingDistance_simple(zoom), + zoom, ); const current = hit @@ -705,7 +705,7 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = ( globalPoint, elements, elementsMap, - maxBindingDistance_simple(appState.zoom), + appState.zoom, ); const pointInElement = hit && diff --git a/packages/element/src/collision.ts b/packages/element/src/collision.ts index ebcacf47b5..afa46c986b 100644 --- a/packages/element/src/collision.ts +++ b/packages/element/src/collision.ts @@ -25,7 +25,7 @@ import type { Radians, } from "@excalidraw/math"; -import type { FrameNameBounds } from "@excalidraw/excalidraw/types"; +import type { AppState, FrameNameBounds } from "@excalidraw/excalidraw/types"; import { isPathALoop } from "./utils"; import { @@ -59,7 +59,7 @@ import { LinearElementEditor } from "./linearElementEditor"; import { distanceToElement } from "./distance"; -import { getBindingGap } from "./binding"; +import { getBindingGap, maxBindingDistance_simple } from "./binding"; import { hasBackground } from "./comparisons"; @@ -253,7 +253,7 @@ export const hitElementBoundText = ( return isPointInElement(point, boundTextElement, elementsMap); }; -const borderDistance = ( +const bindableElementBorderDistanceIfClose = ( element: NonDeleted, point: GlobalPoint, elementsMap: ElementsMap, @@ -339,12 +339,16 @@ export const getHoveredElementForBinding = ( point: Readonly, elements: readonly Ordered[], elementsMap: NonDeletedSceneElementsMap, - tolerance?: number, + zoom?: AppState["zoom"], ): NonDeleted | null => { - const candidateElements: { + type Candidate = { element: NonDeleted; distance: number; - }[] = []; + overlapPercent?: number; + relativeArea?: number; + }; + + const candidates: Candidate[] = []; for (let index = elements.length - 1; index >= 0; --index) { const element = elements[index]; @@ -352,38 +356,42 @@ export const getHoveredElementForBinding = ( continue; } - const distance = borderDistance( + const maxDistance = maxBindingDistance_simple(zoom); + const distance = bindableElementBorderDistanceIfClose( element, point, elementsMap, - tolerance ?? 0, + maxDistance, ); - const bindingGap = getBindingGap(element, arrow); - if (distance > -(tolerance ?? bindingGap)) { - candidateElements.push({ element, distance }); + if (distance > -maxDistance) { + candidates.push({ element, distance }); + + if (!isTransparent(element.backgroundColor) && distance >= 0) { + break; + } } } - if (candidateElements.length === 0) { + if (candidates.length === 0) { return null; } - if (candidateElements.length === 1) { - return candidateElements[0].element; + if (candidates.length === 1) { + return candidates[0].element; } - const closestDistance = candidateElements.sort( + const closestElements = candidates.sort( (a, b) => Math.abs(a.distance) - Math.abs(b.distance), ); - const candidate = closestDistance[0]; + const candidate = closestElements[0]; const [cx1, cy1, cx2, cy2] = getElementBounds(candidate.element, elementsMap); const candidateArea = Math.max( 0.00001, Math.abs(cx2 - cx1) * Math.abs(cy2 - cy1), ); - const overlaps = closestDistance + const overlaps = closestElements .map((c) => { if (c.element === candidate.element) { return { ...c, overlapPercent: 0, relativeArea: 1 }; diff --git a/packages/element/src/elbowArrow.ts b/packages/element/src/elbowArrow.ts index d4034920ac..c0e53ee648 100644 --- a/packages/element/src/elbowArrow.ts +++ b/packages/element/src/elbowArrow.ts @@ -30,7 +30,6 @@ import { getHeadingForElbowArrowSnap, getGlobalFixedPointForBindableElement, getBindingGap, - maxBindingDistance_simple, BASE_BINDING_GAP_ELBOW, } from "./binding"; import { distanceToElement } from "./distance"; @@ -2286,7 +2285,7 @@ const getHoveredElement = ( origPoint, elements, elementsMap, - maxBindingDistance_simple(zoom), + zoom, ); }; diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index db7a342d41..e29dca5521 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -250,7 +250,6 @@ import { getElementBounds, doBoundsIntersect, isPointInElement, - maxBindingDistance_simple, convertToExcalidrawElements, type ExcalidrawElementSkeleton, getSnapOutlineMidPoint, @@ -7092,7 +7091,7 @@ class App extends React.Component { pointFrom(scenePointerX, scenePointerY), this.scene.getNonDeletedElements(), elementsMap, - maxBindingDistance_simple(this.state.zoom), + this.state.zoom, ); if (hoveredElement) { this.setState({ @@ -7132,7 +7131,7 @@ class App extends React.Component { pointFrom(scenePointerX, scenePointerY), this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap(), - maxBindingDistance_simple(this.state.zoom), + this.state.zoom, ); if (hoveredElement) { this.actionManager.executeAction(actionFinalize, "ui", { @@ -7284,7 +7283,7 @@ class App extends React.Component { pointFrom(scenePointerX, scenePointerY), this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap(), - maxBindingDistance_simple(this.state.zoom), + this.state.zoom, ); const scenePointer = pointFrom(scenePointerX, scenePointerY); const elementsMap = this.scene.getNonDeletedElementsMap(); @@ -9893,7 +9892,7 @@ class App extends React.Component { pointFrom(pointerCoords.x, pointerCoords.y), this.scene.getNonDeletedElements(), elementsMap, - maxBindingDistance_simple(this.state.zoom), + this.state.zoom, ); this.handleDelayedBindModeChange(element, hoveredElement);