I have an Animated Flashlist created using Reanimated:-
import Reanimated from 'react-native-reanimated'
const AnimatedFlashlist = Reanimated.createAnimatedComponent(FlashList);
The renderItem
of this Flashlist contains 5 views in a row. Among those, two views are supposed to be draggable. It looks mostly like this:-
<View1></View1>
<DraggableView1></DraggableView1>
<View2></View2>
<DraggableView2></DraggableView2>
<View3></View3>
There are about 70 elements in the Flashlist.
Now, each draggable view is wrapped with a GestureDetector
taken from react-native-gesture-handler
<GestureDetector gesture={dragGesture}>
<SomeJSX/>
</GestureDetector>
What is the dragGesture assigned to it?
- It is a Gesture.Pan() with its own onStart, onBegin, onEnd
Each DraggableView is wrapped with its own instance of Gesture.Pan() That's because each view should be draggable on its own.
The GestureHandlerRootView
has been wrapped over the entire app.
What is the issue that I am facing?
- I am supposed to implement auto scrolling when I drag one of the views reaches the top edge or bottom edge of the screen.
- However, even though the scrolling starts, after some time, I lose access to the Pan Gesture and none of the onEnd or onFinalize gets called.
- This doesn't happen at the starting of the auto scroll. It happens after auto scroll has happened 3 to 4 times
How am I auto scrolling?
const scrollFlatList = (cur: number, direction: 'up' | 'down') => {
const offsetChange = 20;
const newOffset = direction === 'up' ? cur - offsetChange : cur + offsetChange;
flatListRef.current?.scrollToOffset({ offset: newOffset, animated: true });
};
const shouldAutoScroll = useDerivedValue(() => {
const isDraggingActive = isDragging.value;
const isAtTopEdge = dragY.value < SCROLL_THRESHOLD + 30;
const isAtBottomEdge = dragY.value > height - SCROLL_THRESHOLD;
return isDraggingActive && (isAtTopEdge || isAtBottomEdge);
});
useDerivedValue(() => {
if (!shouldAutoScroll.value) {
return;
}
const now = Date.now();
if (now - lastScrollTime.value < 300) {
return; // Prevent scrolling too frequently (every 300ms)
}
lastScrollTime.value = now;
// let targetOffset;
if (dragY.value > height - SCROLL_THRESHOLD) {
scrollTarget.value = scrollOffset.value + 20;
runOnJS(scrollFlatList)(scrollTarget.value, 'down');
} else if (dragY.value < SCROLL_THRESHOLD + 30) {
scrollTarget.value = scrollOffset.value - 20;
runOnJS(scrollFlatList)(scrollTarget.value, 'up');
} else {
// runOnJS(stopScrolling)();
}
});
My dragGesture looks mostly like this and I am updating the dragY value in onUpdate. dragY is a shared value. I am keeping all calculations on UI thread
Gesture.Pan()
.onStart(()=>{
// initial value assignment of shared values
})
.onUpdate((event)=>{
dragY.value = event.absoluteY
})
.onEnd(()=>{
// resetting shared values
})
When scrolling starts, after sometime, onEnd never gets called. It seems like scrolling steals the PanGesture. None of the onTouchesCancelled
or onFinalize
also gets called.
Please help here. Versions used:-
- "react-native-reanimated": "3.8.0"
- "react-native-gesture-handler": "2.17.1"
I am doing all this on iOS