PanResponder :
PanResponder works in combination with the Animated API to help generate complex UI movements. The example below contains an animated View part that can be freely dragged across the screen
Example
import React, {useState, useEffect, useRef} from 'react';
import {
View,
Text,
Dimensions,
Animated,
PanResponder,
ScrollView,
Image,
TouchableOpacity,
} from 'react-native';
const {width, height} = Dimensions.get('window');
import Icon from 'react-native-vector-icons/Ionicons';
const App = (props) => {
const onPressPlay = () => {
setIsPlay(!isPlay);
};
const [isPlay, setIsPlay] = useState(true);
const [isScrollEnabled, setIsScrollEnabled] = useState(false);
const animation = useRef(new Animated.ValueXY({x: 0, y: height - 90}))
.current;
const scrollOffset = 0;
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => {
if (
(isScrollEnabled && scrollOffset <= 0 && gestureState.dy > 0) ||
(!isScrollEnabled && gestureState.dy < 10)
) {
console.log('isScrollEnabled', isScrollEnabled);
console.log('!isScrollEnabled', !isScrollEnabled);
console.log('scrollOffset', scrollOffset);
console.log('gestureState DY', gestureState);
return true;
} else {
return false;
}
},
onPanResponderGrant: (evt, gestureState) => {
animation.extractOffset();
},
onPanResponderMove: (evt, gestureState) => {
animation.setValue({x: 0, y: gestureState.dy});
},
onPanResponderRelease: (evt, gestureState) => {
if (gestureState.moveY > height - 120) {
Animated.spring(animation.y, {
toValue: 0,
tension: 1,
useNativeDriver: false,
}).start();
} else if (gestureState.moveY < 120) {
console.log('gestureState', gestureState);
Animated.spring(animation.y, {
toValue: 0,
tension: 1,
useNativeDriver: false,
}).start();
} else if (gestureState.dy < 0) {
setIsScrollEnabled(false);
Animated.spring(animation.y, {
toValue: -height + 120,
tension: 1,
useNativeDriver: false,
}).start();
} else if (gestureState.dy > 0) {
setIsScrollEnabled(false);
Animated.spring(animation.y, {
toValue: height - 120,
tension: 1,
useNativeDriver: false,
}).start();
}
},
}),
).current;
const animatedHeight = {
transform: animation.getTranslateTransform(),
};
const animatedImageHeight = animation.y.interpolate({
inputRange: [0, height - 90],
outputRange: [200, 32],
extrapolate: 'clamp',
});
const animatedSongTitleOpacity = animation.y.interpolate({
inputRange: [0, height - 500, height - 90],
outputRange: [0, 0, 1],
extrapolate: 'clamp',
});
const animatedImageMarginLeft = animation.y.interpolate({
inputRange: [0, height - 90],
outputRange: [width / 2 - 100, 10],
extrapolate: 'clamp',
});
const animatedSongDetailsOpacity = animation.y.interpolate({
inputRange: [0, height - 500, height - 90],
outputRange: [1, 0, 0],
extrapolate: 'clamp',
});
const animatedHeaderHeight = animation.y.interpolate({
inputRange: [0, height - 90],
outputRange: [height / 3, 90],
extrapolate: 'clamp',
});
console.log('panResponder:;', panResponder.panHandlers);
return (
<View style={{flex: 1, backgroundColor: '#f8f8ff'}}>
<Animated.View
{...panResponder.panHandlers}
style={[
animatedHeight,
{
position: 'absolute',
left: 0,
right: 0,
zIndex: 10,
backgroundColor: '#fff',
height: height,
},
]}>
<ScrollView
scrollEnabled={isScrollEnabled}
scrollEventThrottle={16}
onScroll={(event) => {
scrollOffset = event.nativeEvent.contentOffset.y;
}}>
<Animated.View
style={{
height: animatedHeaderHeight,
borderTopWidth: 1,
borderTopColor: '#ebe5e5',
flexDirection: 'row',
alignItems: 'center',
}}>
<View style={{flex: 4, flexDirection: 'row', alignItems: 'center'}}>
<Animated.View
style={{
height: animatedImageHeight,
width: animatedImageHeight,
marginLeft: animatedImageMarginLeft,
}}>
<Image
style={{flex: 1, width: null, height: null}}
source={{
uri:
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTfCrFGpWG5cxEH5GpVjNU8wFutQebq-sn1ww&usqp=CAU',
}}
/>
</Animated.View>
<Animated.Text
style={{
opacity: animatedSongTitleOpacity,
fontSize: 18,
paddingLeft: 10,
}}>
I Love You song
</Animated.Text>
</View>
<Animated.View
style={{
opacity: animatedSongTitleOpacity,
flex: 1,
flexDirection: 'row',
justifyContent: 'space-around',
}}>
<TouchableOpacity onPress={() => onPressPlay()}>
<Icon
name={isPlay ? 'play-outline' : 'pause-outline'}
size={32}
/>
</TouchableOpacity>
</Animated.View>
</Animated.View>
<Animated.View
style={{
height: animatedHeaderHeight,
opacity: animatedSongDetailsOpacity,
}}>
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'flex-end',
}}>
<Text style={{fontWeight: 'bold', fontSize: 22}}>
Music Player Is (On)
</Text>
<Text style={{fontSize: 18, color: '#fa95ed'}}>
despacito despacito
</Text>
</View>
<View
style={{height: 40, width: width, alignItems: 'center'}}></View>
<View
style={{
flex: 0.5,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
}}>
<TouchableOpacity>
<Icon name={'play-skip-back-outline'} size={32} />
</TouchableOpacity>
<TouchableOpacity onPress={() => onPressPlay()}>
<Icon
name={isPlay ? 'play-outline' : 'pause-outline'}
size={32}
/>
</TouchableOpacity>
<TouchableOpacity>
<Icon name={'play-skip-forward-outline'} size={32} />
</TouchableOpacity>
</View>
</Animated.View>
</ScrollView>
</Animated.View>
</View>
);
};
export default App;
o
nResponder*
callbacks. For example, the config
object would look like:onMoveShouldSetPanResponder: (e, gestureState) => {...}
onMoveShouldSetPanResponderCapture: (e, gestureState) => {...}
onStartShouldSetPanResponder: (e, gestureState) => {...}
onStartShouldSetPanResponderCapture: (e, gestureState) => {...}
onPanResponderReject: (e, gestureState) => {...}
onPanResponderGrant: (e, gestureState) => {...}
onPanResponderStart: (e, gestureState) => {...}
onPanResponderEnd: (e, gestureState) => {...}
onPanResponderRelease: (e, gestureState) => {...}
onPanResponderMove: (e, gestureState) => {...}
onPanResponderTerminate: (e, gestureState) => {...}
onPanResponderTerminationRequest: (e, gestureState) => {...}
onShouldBlockNativeResponder: (e, gestureState) => {...}
“Amazing write-up!”
ReplyDelete“Useful post”
ReplyDelete“Great share!”
ReplyDelete“Nice info!”
ReplyDeleteA Modest Proposal.
ReplyDelete