I have a simple horizontal slider built using the ScrollView component. There is a button to go to the next slide and when pressed I call scrollTo method on that ScrollView component. I also need to detect when the scroll is finished to display some other animations. I tried using onMomentumScrollEnd and onScrollEndDrag callbacks but on android neither of them is called when I use scrollTo method. Is there any other way to detect scroll 'end' event?
Here is simple example on snack: https://snack.expo.io/#levani/forlorn-bacon
Try this solution, i've modified your snack
I've used onSroll listener with the function :
const scrollDirection = (event) => {
const offsetX = event.nativeEvent.contentOffset.x
const dif = offsetX - (offset || 0);
console.log('dif',dif)
if (dif == 200) {
alert('destination reached')
}
setOffset(offsetX);
};
Related
i would like to know if there is any way to stop scroll event on iOs and Android
Steps to reproduce
scroll FlatList / ScrollView
click any button
already tried "onScroll" but when i set scroll ref.scrollTo it will scroll when stroll stops
Example GIF:
This is just an idea which I think might work, you may have to test it yourself.
Assuming you have the following state, controlling the modal.
const [open, setOpen] = useState(false);
You can probably add a onScroll event to Flatlist (onScroll is inherited from ScrollView), then
const onScroll = (e) => {
if (open) e.preventDefault();
}
If there is a delay before e.preventDefault is called, since setting state is async, you may want to use useRef() instead of useState() for checking if (open) since scroll happens quickly.
Another idea is to probably use a listener for the flat list, to listen to scroll, using same logic above.
We have already developed a swiper using React Native PanResponder, (before somebody comment) - we cannot use any other swiper library here. In the swiper we developed, we are facing an issue when a user ends the swipe(meaning releases the Pan) there is a lag when starting a spring animation.
So to fix that, we are trying to move from React Native Animated API to Reanimated Libary which could solve the problem of lag the user is facing.
But while developing with React Native Gesture Handler (PanGestureHandler) and
Reanimated Library, we're unable to detect swipe direction in the
gesture handler.
I'm adding the part of code which we used to detect swipe direction in PanResponder
onPanMoving = (evt, {dx}) => {
this.draggedEnd = dx < -this.swipeThreshold; //You are moving towards right screen
this.draggedStart = dx > this.swipeThreshold; //You are moving towards left screen
}
As you see, detecting the direction while the pan is moving was so easy in PanResponder.
Here comes the problem with Gesture Handler,
I'm unable to detect swipe while the gesture state is active
I have already searched for issues in Gesture Handler and I found this.
There were two workaround suggested in the issue
First one is by Sonaye which has two handler and detects the
swipe direction in onHandlerStateChange for both the handlers
which was not helpful while using reanimated because it only sets swipe direction
when handlers state changes.
Second one is by Satya which actually detects the swipe when
the State is ACTIVE but he uses translationX property, the
translationX property can also negative for us and can move in
either direction similar to swiper.
Both the workarounds doesn't solve our problem.
So is there any other way to find direction using PanGestureHandler and Reanimated. I tried using PanResponder with Reanimated with dx value but ended up with error message i.e nativeEvent properties are only support as dx is from gestureState param from PanResponder.
Note: We cannot use FlingGestureHandler and Swipeables because we need to find direction in Fling still onHandlerStateChange and Swipeables doesn't use Reanimated.
You can detect direction when swipe horizontal via property velocityX
Example :
const handleGesture = (evt) => {
const { nativeEvent } = evt;
if (nativeEvent.velocityX > 0) {
console.log('Swipe right');
} else {
console.log('Swipe left');
}
};
return (
<PanGestureHandler onGestureEvent={handleGesture}>
// child component
</PanGestureHandler>
);
For those who want to use double FlingGestureHandler to have vertical or horizontal fling gesture here is the solution:
First you have to initialize the stateHandlers for the direction you want. In my case, i was trying to implement a vertical flingGestureHandler.
const onDownFlingHandlerStateChange = ({
nativeEvent,
}: FlingGestureHandlerStateChangeEvent) => {
if (nativeEvent.oldState === State.ACTIVE) {
progressHeight.value = withTiming(COLLAPSED_HEIGHT, {
duration: ANIMATION_DURATION,
});
}
};
const onUpFlingHandlerStateChange = ({
nativeEvent,
}: FlingGestureHandlerStateChangeEvent) => {
if (nativeEvent.oldState === State.ACTIVE) {
progressHeight.value = withTiming(
DEVICE_HEIGHT - TOP_PADDING_FOR_EXPANDED,
{
duration: ANIMATION_DURATION,
},
);
}
};
and then you have to bind those handlers to 2 FlingGestureHandler component of RNGH
<FlingGestureHandler
onHandlerStateChange={onUpFlingHandlerStateChange}
onEnded={() => {
onPressChecklistIcon();
}}
direction={Directions.UP}>
<FlingGestureHandler
direction={Directions.DOWN}
onEnded={() => {
onPressChecklistIcon();
}}
onHandlerStateChange={onDownFlingHandlerStateChange}>
<Animated.View style={[styles.container, reanimatedStyle]}>
<View style={styles.closerContainer}>
<View style={styles.closer} />
</View>
{renderChecklistContent()}
</Animated.View>
</FlingGestureHandler>
</FlingGestureHandler>
Use translationX property rather than velocityX property by nativeEvent:
const handleGesture = (evt) => {
const { nativeEvent } = evt;
if (nativeEvent.translationX > 0) {
Alert.alert('Swipe right');
} else {
Alert.alert('Swipe left');
}
};
In my case I have used double FlingGestureHandler to detect the direction. It's worked for me.
I set flatlist's data in the constructor, and call flatlist scrollToEnd in the componentDidMount method, I think it should scroll to end, but it did't. And I click one button to call scrollToEnd method, the flatlist scroll to end.
So I want to know when should I call flatlist's method to change it without click.
try scrollToEnd with timeout
componentDidMount() {
setTimeout(() => {
this.refs.list.scrollToEnd();
}, 100);
}
When you call without timeout your FlatList not render at that time so scrollToEnd not working
I'm using onLayout to detect screen orientation and it's working fine inside my root view, but when I implemented inside the drawer it didn't work, any reason why this happens ?
code :
import Drawer from 'react-native-drawer'
...
onLayout(e) {
console.log('onLayout');
}
<Drawer onLayout={this.onLayout}
It didn't log any thing when orientation changed!
This is because the Drawer component doesn't take onLayout as a prop. You can see in the source code that the rendered View does use onLayout, but it's not pulling from something like this.props.onLayout.
I'm not exactly sure what you're looking to do, but maybe this issue will help you. As it shows, you can pass a function into openDrawerOffset instead of an integer or a ratio in order to be a little more dynamic with how you set your offset:
openDrawerOffset={(viewport) => {
if (viewport.width < 400) {
return viewport.width * 0.1;
}
return viewport.width - 400;
}}
You might also benefit from the Event handlers that react-native-drawer has to offer.
I'm developing a React Native App using Redux and I want to do a simple task:
Collapse or close a search bar of another component depending on the Y position of a ScrollView.
We have a ListView:
<ScrollView style={styles.scene} onScroll={(event) => {this.onScroll(event)}}>
and this function:
onScroll(event) {
var currentOffset = event.nativeEvent.contentOffset.y;
var direction = currentOffset > this.offset ? 'down' : 'up';
this.offset = currentOffset;
if(currentOffset == 0 && !this.props.isCerca && this.props.textInputTextShop != ""){
this.props.setCerca(true);
}else{
if(currentOffset >50 && this.props.isCerca) this.props.setCerca(false);
}
}
The main problem is that:
When we reach the 0 on the Y (Top of the ListView) the searchbar will be closed BUT if we keep the finger on the screen this event will be fired a lot of times. When we call the function this.props.setCerca(false); the reducer takes time to modify the boolean this.props.isCerca so the searchbar will be collapsed multiple times causing a strange behavior.
How I can set this boolean immediately using redux to returning all the next times without recalling the reducer? Is there a way to "wait" a reducer action? I have to use a local component variable?
Thank you.