You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Double-tap zoom on Gallery is cancelled almost immediately: scale moves off 1 for few frames, then reset forces it back to 1. Pinch zoom is not affected and stays at the pinched scale.
video_2026-09-07_19-04-55-optimized.mp4
What happens
GalleryGestureHandler resets transform on every rootSize write:
measureRoot (onLayout) always does rootSize.width.set(width) / height.set(height), even when the size did not change. In Reanimated 3/4, assigning the same number still notifies listeners, so this reaction fires and cancels the in-flight double-tap animation.
Observed:
Double-tap starts withTiming toward maxScale, then scale is forced to 1 (onZoomBegin at ~1.000…, immediately onZoomEnd at 1). Zoom never reaches maxScale.
Pinch zoom works and keeps its scale. The reset only shows up on double-tap.
Reset is still correct on a real size change (rotation / resize), but not on a repeated layout with the same width/height.
Pan activates on tiny movement, so single tap / double-tap often lose the race and onTap never fires. Pinch already uses manualActivation (2 touches only), so it does not need to race against taps.
Also, onDoubleTapStart / onDoubleTapEnd exist on GalleryProps but are not forwarded from Gallery.tsx into GalleryGestureHandler.
Suggested fix
In the reaction, compare current vs previous and skip reset when width/height are unchanged (and on the first run).
In measureRoot, return early if rootSize already has the same width/height.
Optional: Simultaneous(pinch, Race(pan, taps)) + pan.minDistance(10) so taps can win without delaying swipe. Exclusive(taps, pan) is a bad substitute — pan then waits for the double-tap timeout (~250ms) and swipes break.
Happy to open a PR if this direction is ok.
Environment
Platform: Android
react-native-zoom-toolkit: 5.1.1
react-native: 0.87.0
react: 19.2.3
react-native-reanimated: 4.6.0
react-native-gesture-handler: 3.2.1
react-native-worklets: 0.12.1
Steps to Reproduce
Render Gallery with at least two images, zoomEnabled, default maxScale.
Double-tap an item.
Observe scale: one frame above 1, then back to 1. onZoomBegin fires, then immediately onZoomEnd. Zoom never reaches maxScale.
Pinch the same item to ~3×: scale stays. The bug is double-tap only.
Summary
Double-tap zoom on
Galleryis cancelled almost immediately: scale moves off1for few frames, thenresetforces it back to1. Pinch zoom is not affected and stays at the pinched scale.video_2026-09-07_19-04-55-optimized.mp4
What happens
GalleryGestureHandlerresets transform on everyrootSizewrite:measureRoot(onLayout) always doesrootSize.width.set(width)/height.set(height), even when the size did not change. In Reanimated 3/4, assigning the same number still notifies listeners, so this reaction fires and cancels the in-flight double-tap animation.Observed:
withTimingtowardmaxScale, then scale is forced to1(onZoomBeginat~1.000…, immediatelyonZoomEndat1). Zoom never reachesmaxScale.Secondary: tap / double-tap lose
Gesture.RacePan activates on tiny movement, so single tap / double-tap often lose the race and
onTapnever fires. Pinch already usesmanualActivation(2 touches only), so it does not need to race against taps.Also,
onDoubleTapStart/onDoubleTapEndexist onGalleryPropsbut are not forwarded fromGallery.tsxintoGalleryGestureHandler.Suggested fix
currentvspreviousand skipresetwhen width/height are unchanged (and on the first run).measureRoot, return early ifrootSizealready has the same width/height.Simultaneous(pinch, Race(pan, taps))+pan.minDistance(10)so taps can win without delaying swipe.Exclusive(taps, pan)is a bad substitute — pan then waits for the double-tap timeout (~250ms) and swipes break.Happy to open a PR if this direction is ok.
Environment
Steps to Reproduce
Gallerywith at least two images,zoomEnabled, defaultmaxScale.1, then back to1.onZoomBeginfires, then immediatelyonZoomEnd. Zoom never reachesmaxScale.