I am researching about how to detect a finger move into a view in React Native. For example, I have a single view in screen. My finger press outside the view and slowly move to the view. The view can recognize my finger when the finger cross the border.
I researched about Gesture Responder and PanResponder. But all the examples I found are touching on the view and moving it.
If you have a way or an example code to do what I said above, please let me know.
Do i need to do some mathematical calculations to reach the destination.?
Thank you in advance!
The previous comments by @LuongTrong touched on this, but it's not as hard as it sounds, once you get a handle on what the PanResponder does and how it works. The way you need to detect when the user drags their finger over a view is using a PanResponder, view.onLayout and view References. When the view you want to track touches into and out of loads, capture the 4 corners of the view (onLayout gives you x,y and height, width so some addition is required). Using the onPanResponderMove method, check whether or not the touchpoint is in the bounds of the view, then update the ref's state accordingly.
It sounds more complicated than it is, here's an App.js (100% demo code) that demonstrates how this works. I've broken the "button" components out to a separate component to make it more reusable.
import React, {useEffect, useState} from 'react'
import {setStatusBarBackgroundColor, StatusBar} from 'expo-status-bar'
import {PanResponder, Pressable, StyleSheet, Text, TouchableOpacity, View} from 'react-native'
import {GestureHandlerRootView, PanGestureHandler} from 'react-native-gesture-handler'
import {useEvent} from 'react-native-reanimated'
const CustButton = React.forwardRef((props, ref) => {
const [viewLayout, setViewLayout] = useState({})
const selColor = 'red'
const unselectedColor = 'blue'
const [selectedColor, setSelectedColor] = useState(unselectedColor)
function getViewLatout() {
return viewLayout
}
const updateSelectionStatus = (isActive) => {
setSelectedColor(isActive ? selColor : unselectedColor)
}
const checkPoint = (x,y) => {
const {top, left, right, bottom} = viewLayout
return (x > left && x < right && y > top && y < bottom)
}
React.useImperativeHandle(ref, () => ({
// each key is connected to `ref` as a method name
// they can execute code directly, or call a local method
setActive: (isActive) => { updateSelectionStatus(isActive) },
isPointInView: (x,y) => { return checkPoint(x,y)}
}))
return (<View
ref={ref}
style={[styles.button, {backgroundColor: selectedColor}]}
onLayout={(evt) => {
const {x, y, height, width} = evt.nativeEvent.layout
const viewLayoutProp = {
top: y,
bottom: y + height,
right: x + width,
left: x
}
setViewLayout(viewLayoutProp)
}}>
<Text style={{color: 'white', fontSize: 24, fontWeight: 'bold'}}>move over me</Text>
</View>)
})
export default function App() {
const viewRef = React.useRef()
const viewRef2 = React.useRef()
const viewRef3 = React.useRef()
const checkIfAViewIsSelected = (evt) => {
const {pageX:x, pageY:y} = evt.nativeEvent
viewRef.current.setActive(viewRef.current.isPointInView(x,y))
viewRef2.current.setActive(viewRef2.current.isPointInView(x,y))
viewRef3.current.setActive(viewRef3.current.isPointInView(x,y))
}
const panResponder = React.useMemo(() =>
PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {
},
onPanResponderStart:(evt) => {
checkIfAViewIsSelected(evt)
},
onPanResponderMove: (evt, gestureState) => {
checkIfAViewIsSelected(evt)
}
}), [viewRef, viewRef2, viewRef3])
useEffect(() => {
if (viewRef.current == null) {
return;
}
}, [viewRef.current])
return (
<View style={styles.container}>
<View style={[styles.container, {backgroundColor: '#fff'}]}
{...panResponder.panHandlers}
>
<CustButton ref={viewRef} />
<CustButton ref={viewRef2} />
<CustButton ref={viewRef3} />
<StatusBar style="auto"/>
</View>
</View>
)
}
const styles = StyleSheet.create(
{
container: {
flex: 0,
backgroundColor: '#555',
alignItems: 'center',
justifyContent: 'center',
gap: 20,
height: '100%',
width:'100%'
},
button: {
flex: 0,
height: 100,
width: 250,
backgroundColor: 'red',
alignItems: 'center',
justifyContent: 'center'
}
})
The real gotcha's in here are:
Hope this helps you, or someone else who happens to stumble into this post.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With