Add contentX/contentY to ZoomableViewEvent#181
Conversation
Expose the gesture's first touch in content (bitmap) coordinates on ZoomableViewEvent so consumers can tell where the user tapped on the contents. Populated when the callback has an associated gesture event and both contentWidth/contentHeight props are set; undefined otherwise. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR SummaryLow Risk Overview Updates Reviewed by Cursor Bugbot for commit 2903b19. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 2903b19. Configure here.
`{}` placeholders showed up at almost every call site after adding the
gesture-event parameter — a signal the override slot was carrying its
weight for exactly one caller (`onDoubleTapAfter`, which substitutes the
target `zoomLevel`). Let that one caller spread the override externally
and simplify the helper to just `(gestureEvent?)`.
Also drops the redundant override in `_staticPinPosition` that re-passed
the same SharedValues the function already reads.
Side effect: at `onDoubleTapAfter`, `contentX`/`contentY` are now
computed using the current zoom level rather than the target, which
matches the touch's actual content-space position (the touch occurred
before the zoom animation started).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| // size, so it returns null pre-measurement or with no content size set — | ||
| // leave `contentX`/`contentY` undefined in that case rather than emit | ||
| // NaN/Infinity to consumers. | ||
| const touch = gestureEvent?.allTouches[0]; |
There was a problem hiding this comment.
🟡 we only need x and y from the touch here. Passing the whole event could lead to misinterpretation of the event when the first touch is not intended to be the source for the x and y.
There was a problem hiding this comment.
What do you mean by passing the whole event? We're only passing this touch to viewportPositionToImagePosition to calculate the content position?
There was a problem hiding this comment.
Sorry I meant passing the whole event into the getZoomableViewObject function
| offsetY: offsetY.value, | ||
| zoomLevel: zoom.value, | ||
| }), | ||
| zoomableEvent: _getZoomableViewEventObject(), |
There was a problem hiding this comment.
🟣 _staticPinPosition can probably be replaced by _getZoomableViewEventObject(staticPinPosition.value) making the interface for static pin more standardized
There was a problem hiding this comment.
Not without changing the _getZoomableViewEventObject because it needs a GestureTouchEvent we don't have?
| * programmatic `zoomTo()`) and when content dimensions are unknown. | ||
| */ | ||
| contentX?: number; | ||
| contentY?: number; |
There was a problem hiding this comment.
🟣 this might create confusion from the client in the form of: "is there a time where we have contentX but not contentY?" it's probably best to use TS to make them either both undefined or exists, or even better, group them in a property
There was a problem hiding this comment.
Maybe all this complexity is an indicator that ZoomableViewEvent is doing too much. Thinking about it - why would onSingleTap need to return originalHeight and originalWidth. This is probably a flawed legacy design we need to move away in v3.
There was a problem hiding this comment.
I don't know, but I imagine it's so that the consumer can have all the info it needs packed into the event. Which I think is fairly reasonable if you already have an event object. It could definitely be better arranged, that's for sure. I don't really know why most of these attributes are exposed to the client
There was a problem hiding this comment.
Separate optional values is okay here, it's consistent with contentWidth? / contentHeight? which are both optional. They're all flat in this interface:
initialOffsetX?: number;
initialOffsetY?: number;
contentWidth?: number;
contentHeight?: number;|
@thomasttvo Scratch that — the touchData is part of |

Summary
contentX/contentYtoZoomableViewEventcarrying the gesture's first touch in content (bitmap) coordinates — so consumers can tell where the user tapped on the contents.GestureTouchEventAND bothcontentWidthandcontentHeightprops are set. Computed via the existingviewportPositionToImagePositionhelper (samecontainresize-mode assumption).onTransformWorklet,onZoomEndafter a programmaticzoomTo()completes naturally) and when content dimensions aren't configured.Test plan
onSingleTapwithcontentX/contentYset whencontentWidth/contentHeightare configured.contentX/contentYare undefined whencontentWidth/contentHeightare not set.contentX/contentYare undefined ononZoomEndfired from programmaticzoomTo()completion (no gesture event).🤖 Generated with Claude Code