-
Notifications
You must be signed in to change notification settings - Fork 63
Add contentX/contentY to ZoomableViewEvent #181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -344,20 +344,41 @@ const ReactNativeZoomableViewInner: ForwardRefRenderFunction< | |
| * @private | ||
| */ | ||
| const _getZoomableViewEventObject = ( | ||
| overwriteObj: Partial<ZoomableViewEvent> = {} | ||
| gestureEvent?: GestureTouchEvent | ||
| ): ZoomableViewEvent => { | ||
| 'worklet'; | ||
|
|
||
| return Object.assign( | ||
| { | ||
| zoomLevel: zoom.value, | ||
| offsetX: offsetX.value, | ||
| offsetY: offsetY.value, | ||
| originalHeight: originalHeight.value, | ||
| originalWidth: originalWidth.value, | ||
| }, | ||
| overwriteObj | ||
| ); | ||
| const event: ZoomableViewEvent = { | ||
| zoomLevel: zoom.value, | ||
| offsetX: offsetX.value, | ||
| offsetY: offsetY.value, | ||
| originalHeight: originalHeight.value, | ||
| originalWidth: originalWidth.value, | ||
| }; | ||
|
|
||
| // Populate content (bitmap) coordinates of the first touch when caller | ||
| // supplied a gesture event AND content dimensions are known. | ||
| // `viewportPositionToImagePosition` divides by container size and image | ||
| // 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]; | ||
| if (touch && contentWidth.value && contentHeight.value) { | ||
| const contentPos = viewportPositionToImagePosition({ | ||
| viewportPosition: { x: touch.x, y: touch.y }, | ||
| imageSize: { | ||
| width: contentWidth.value, | ||
| height: contentHeight.value, | ||
| }, | ||
| zoomableEvent: event, | ||
| }); | ||
| if (contentPos) { | ||
| event.contentX = contentPos.x; | ||
| event.contentY = contentPos.y; | ||
| } | ||
| } | ||
|
|
||
| return event; | ||
| }; | ||
|
|
||
| const _staticPinPosition = () => { | ||
|
|
@@ -381,11 +402,7 @@ const ReactNativeZoomableViewInner: ForwardRefRenderFunction< | |
| height: contentHeight.value, | ||
| width: contentWidth.value, | ||
| }, | ||
| zoomableEvent: _getZoomableViewEventObject({ | ||
| offsetX: offsetX.value, | ||
| offsetY: offsetY.value, | ||
| zoomLevel: zoom.value, | ||
| }), | ||
| zoomableEvent: _getZoomableViewEventObject(), | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟣 _staticPinPosition can probably be replaced by
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not without changing the |
||
| }); | ||
| }; | ||
|
|
||
|
|
@@ -768,7 +785,7 @@ const ReactNativeZoomableViewInner: ForwardRefRenderFunction< | |
| // `props.onLongPress` — the closure was captured at schedule time | ||
| // and would fire a stale callback if the parent re-rendered during | ||
| // the timer window. | ||
| onLongPress(e, _getZoomableViewEventObject()); | ||
| onLongPress(e, _getZoomableViewEventObject(e)); | ||
| }, props.longPressDuration); | ||
| } | ||
| }); | ||
|
|
@@ -828,7 +845,7 @@ const ReactNativeZoomableViewInner: ForwardRefRenderFunction< | |
| // the resumed gesture. | ||
| runOnJS(setGestureStartedJS)(true); | ||
| if (!isRecovery) { | ||
| runOnJS(_safeOnPanResponderGrant)(e, _getZoomableViewEventObject()); | ||
| runOnJS(_safeOnPanResponderGrant)(e, _getZoomableViewEventObject(e)); | ||
| } | ||
|
|
||
| cancelAnimation(zoom); | ||
|
|
@@ -1077,7 +1094,7 @@ const ReactNativeZoomableViewInner: ForwardRefRenderFunction< | |
| const { onDoubleTapBefore, onDoubleTapAfter, doubleTapZoomToCenter } = | ||
| props; | ||
|
|
||
| onDoubleTapBefore?.(e, _getZoomableViewEventObject()); | ||
| onDoubleTapBefore?.(e, _getZoomableViewEventObject(e)); | ||
|
|
||
| const nextZoomStep = getNextZoomStep({ | ||
| zoomLevel: zoom.value, | ||
|
|
@@ -1104,10 +1121,10 @@ const ReactNativeZoomableViewInner: ForwardRefRenderFunction< | |
|
|
||
| publicZoomTo(nextZoomStep, zoomPositionCoordinates); | ||
|
|
||
| onDoubleTapAfter?.( | ||
| e, | ||
| _getZoomableViewEventObject({ zoomLevel: nextZoomStep }) | ||
| ); | ||
| onDoubleTapAfter?.(e, { | ||
| ..._getZoomableViewEventObject(e), | ||
| zoomLevel: nextZoomStep, | ||
| }); | ||
| }); | ||
|
|
||
| /** | ||
|
|
@@ -1187,7 +1204,7 @@ const ReactNativeZoomableViewInner: ForwardRefRenderFunction< | |
|
|
||
| // Invoke the stable `onSingleTap` wrapper rather than the captured | ||
| // `props.onSingleTap` — same staleness reasoning as `onLongPress`. | ||
| onSingleTap(e, _getZoomableViewEventObject()); | ||
| onSingleTap(e, _getZoomableViewEventObject(e)); | ||
| }, props.doubleTapDelay); | ||
| } | ||
| }; | ||
|
|
@@ -1358,12 +1375,12 @@ const ReactNativeZoomableViewInner: ForwardRefRenderFunction< | |
|
|
||
| runOnJS(clearLongPressTimeout)(); | ||
|
|
||
| runOnJS(_safeOnPanResponderEnd)(e, _getZoomableViewEventObject()); | ||
| runOnJS(_safeOnPanResponderEnd)(e, _getZoomableViewEventObject(e)); | ||
|
|
||
| if (gestureType.value === 'pinch') { | ||
| runOnJS(_safeOnZoomEnd)(e, _getZoomableViewEventObject()); | ||
| runOnJS(_safeOnZoomEnd)(e, _getZoomableViewEventObject(e)); | ||
| } else if (gestureType.value === 'shift') { | ||
| runOnJS(_safeOnShiftingEnd)(e, _getZoomableViewEventObject()); | ||
| runOnJS(_safeOnShiftingEnd)(e, _getZoomableViewEventObject(e)); | ||
| } | ||
|
|
||
| // RNGH cancellation: queue `onPanResponderTerminate` HERE — inside the | ||
|
|
@@ -1372,7 +1389,7 @@ const ReactNativeZoomableViewInner: ForwardRefRenderFunction< | |
| // `ref.current.gestureStarted` from inside `onPanResponderTerminate` | ||
| // observes `true`, matching SPECS L157 for the other end-callbacks. | ||
| if (isCancellation) { | ||
| runOnJS(_safeOnPanResponderTerminate)(e, _getZoomableViewEventObject()); | ||
| runOnJS(_safeOnPanResponderTerminate)(e, _getZoomableViewEventObject(e)); | ||
| // wasReleased=false skips the suppression branch above; clear here. | ||
| doubleTapFirstTapReleaseTimestamp.value = undefined; | ||
| doubleTapFirstTap.value = undefined; | ||
|
|
@@ -1414,7 +1431,10 @@ const ReactNativeZoomableViewInner: ForwardRefRenderFunction< | |
| 'worklet'; | ||
|
|
||
| if ( | ||
| onPanResponderMoveWorkletShared.value.fn(e, _getZoomableViewEventObject()) | ||
| onPanResponderMoveWorkletShared.value.fn( | ||
| e, | ||
| _getZoomableViewEventObject(e) | ||
| ) | ||
| ) { | ||
| // Consumer intercepted this move. The early-return below skips the | ||
| // gesture-classification branches that would otherwise assign | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,15 @@ export interface ZoomableViewEvent { | |
| offsetY: number; | ||
| originalHeight: number; | ||
| originalWidth: number; | ||
| /** | ||
| * Position of the gesture's first touch in content (bitmap) coordinates. | ||
| * Populated only when the callback has an associated gesture event AND | ||
| * both `contentWidth` and `contentHeight` props are set. Undefined for | ||
| * non-gesture call sites (`onTransformWorklet`, `onZoomEnd` after a | ||
| * programmatic `zoomTo()`) and when content dimensions are unknown. | ||
| */ | ||
| contentX?: number; | ||
| contentY?: number; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟣 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
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe all this complexity is an indicator that
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Separate optional values is okay here, it's consistent with initialOffsetX?: number;
initialOffsetY?: number;
contentWidth?: number;
contentHeight?: number; |
||
| } | ||
|
|
||
| export type ReactNativeZoomableViewRef = { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 we only need
xandyfrom 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 thexandy.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by passing the whole event? We're only passing this touch to
viewportPositionToImagePositionto calculate the content position?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I meant passing the whole event into the
getZoomableViewObjectfunction