From 12c67386cfff0bad98add70e39460875acb94a17 Mon Sep 17 00:00:00 2001 From: Mark Tolmacs Date: Tue, 21 Oct 2025 18:28:44 +0200 Subject: [PATCH] feat: Bind to frame when frame-bound object hidden part is approached --- packages/element/src/binding.ts | 8 +-- .../excalidraw/renderer/interactiveScene.ts | 69 ++++++++++--------- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/packages/element/src/binding.ts b/packages/element/src/binding.ts index 87b64c0023..1645418ed5 100644 --- a/packages/element/src/binding.ts +++ b/packages/element/src/binding.ts @@ -596,15 +596,15 @@ export const getBindingStrategyForDraggingBindingElementEndpoints = ( return { start: startDragged ? { - mode: "inside", - element: hoveredElement, + mode: "orbit", + element: enclosingFrame, focusPoint: globalPoint, } : start, end: endDragged ? { - mode: "inside", - element: hoveredElement, + mode: "orbit", + element: enclosingFrame, focusPoint: globalPoint, } : end, diff --git a/packages/excalidraw/renderer/interactiveScene.ts b/packages/excalidraw/renderer/interactiveScene.ts index 7404ebacab..e8e016d519 100644 --- a/packages/excalidraw/renderer/interactiveScene.ts +++ b/packages/excalidraw/renderer/interactiveScene.ts @@ -409,44 +409,49 @@ const renderBindingHighlightForBindableElement = ( const radius = 0.5 * (Math.min(element.width, element.height) / 2); // Draw center snap area - context.save(); - context.translate(element.x + appState.scrollX, element.y + appState.scrollY); + if (!isFrameLikeElement(element)) { + context.save(); + context.translate( + element.x + appState.scrollX, + element.y + appState.scrollY, + ); - const PROGRESS_RATIO = (1 / BIND_MODE_TIMEOUT) * remainingTime; + const PROGRESS_RATIO = (1 / BIND_MODE_TIMEOUT) * remainingTime; - context.strokeStyle = "rgba(0, 0, 0, 0.2)"; - context.lineWidth = 1 / appState.zoom.value; - context.setLineDash([4 / appState.zoom.value, 4 / appState.zoom.value]); - context.lineDashOffset = (-PROGRESS_RATIO * 10) / appState.zoom.value; + context.strokeStyle = "rgba(0, 0, 0, 0.2)"; + context.lineWidth = 1 / appState.zoom.value; + context.setLineDash([4 / appState.zoom.value, 4 / appState.zoom.value]); + context.lineDashOffset = (-PROGRESS_RATIO * 10) / appState.zoom.value; - context.beginPath(); - context.ellipse( - element.width / 2, - element.height / 2, - radius, - radius, - 0, - 0, - 2 * Math.PI, - ); - context.stroke(); + context.beginPath(); + context.ellipse( + element.width / 2, + element.height / 2, + radius, + radius, + 0, + 0, + 2 * Math.PI, + ); + context.stroke(); - // context.strokeStyle = "transparent"; - context.fillStyle = "rgba(0, 0, 0, 0.04)"; - context.beginPath(); - context.ellipse( - element.width / 2, - element.height / 2, - radius * (1 - opacity), - radius * (1 - opacity), - 0, - 0, - 2 * Math.PI, - ); + // context.strokeStyle = "transparent"; + context.fillStyle = "rgba(0, 0, 0, 0.04)"; + context.beginPath(); + context.ellipse( + element.width / 2, + element.height / 2, + radius * (1 - opacity), + radius * (1 - opacity), + 0, + 0, + 2 * Math.PI, + ); - context.fill(); + context.fill(); - context.restore(); + context.restore(); + } return { runtime: (state?.runtime ?? 0) + deltaTime,