Last active
July 6, 2022 14:05
-
-
Save topherPedersen/caf43bdce394be96a3f3594f615f2588 to your computer and use it in GitHub Desktop.
Custom Side-Menu / Drawer in React-Native (Demo/Prototype)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState } from 'react'; | |
import { | |
View, | |
SafeAreaView, | |
Dimensions, | |
Image, | |
} from 'react-native'; | |
import { GestureDetector, Gesture } from 'react-native-gesture-handler'; | |
import Animated, { | |
useSharedValue, | |
useAnimatedStyle, | |
useDerivedValue, | |
runOnJS, | |
} from 'react-native-reanimated'; | |
const windowWidth = Dimensions.get('window').width; | |
const windowHeight = Dimensions.get('window').height; | |
const drawerWidth = windowWidth * 0.875; | |
const closedLeftPosition = drawerWidth * -1; | |
const openLeftPosition = closedLeftPosition + drawerWidth; | |
function Drawer(props) { | |
console.log(`props.drawerLeftPosition.value: ${props.drawerLeftPosition.value}`); | |
const animatedStyle = useAnimatedStyle(() => { | |
return { | |
position: 'absolute', | |
height: windowHeight, | |
width: drawerWidth, | |
top: 0, | |
left: props.drawerLeftPosition.value, | |
backgroundColor: 'white', | |
borderRadius: 25, | |
zIndex: 2000, | |
}; | |
}); | |
return ( | |
<Animated.View style={animatedStyle}> | |
{/* Drawer Contents Go Here */} | |
</Animated.View> | |
); | |
} | |
function DrawerBackground(props) { | |
if (!props.drawerVisible) { | |
return null; | |
} | |
const animatedStyle = useAnimatedStyle(() => { | |
return { | |
position: 'absolute', | |
height: windowHeight , | |
width: windowWidth, | |
top: 0, | |
right: 0, | |
backgroundColor: 'black', | |
opacity: 0.50, | |
zIndex: 1000, | |
}; | |
}); | |
return ( | |
<Animated.View style={animatedStyle}> | |
{/* Drawer Contents Go Here */} | |
</Animated.View> | |
); | |
} | |
function App() { | |
const panGestureXPosition = useSharedValue(-1); | |
const panGestureTranslationXPosition = useSharedValue(-1); | |
const listenForOpenDrawerGesture = useSharedValue(true); | |
const openDrawerGestureDetected = useSharedValue(false); | |
const leaveDrawerOpen = useSharedValue(false); | |
const closeDrawerGestureDetected = useSharedValue(false); | |
const [ drawerVisible, setDrawerVisible ] = useState(false); | |
const drawerLeftPosition = useDerivedValue(() => { | |
if (openDrawerGestureDetected.value) { | |
return panGestureTranslationXPosition.value <= drawerWidth ? closedLeftPosition + panGestureTranslationXPosition.value : closedLeftPosition + drawerWidth; | |
} else if (closeDrawerGestureDetected.value) { | |
// EDGE CASE: Don't let user start dragging close, then start dragging back open PAST the maximum open position | |
if (openLeftPosition + panGestureTranslationXPosition.value >= openLeftPosition) { | |
return openLeftPosition; | |
} else if (openLeftPosition + panGestureTranslationXPosition.value >= closedLeftPosition) { | |
return openLeftPosition + panGestureTranslationXPosition.value | |
} else { | |
return closedLeftPosition; | |
} | |
}else if (leaveDrawerOpen.value) { | |
return openLeftPosition; | |
} else { | |
return closedLeftPosition; | |
} | |
}); | |
const gesture = Gesture.Pan() | |
.onBegin(() => { | |
listenForOpenDrawerGesture.value = true; | |
openDrawerGestureDetected.value = false; | |
}) | |
.onUpdate((e) => { | |
panGestureXPosition.value = e.x; | |
panGestureTranslationXPosition.value = e.translationX; | |
const swipeRight = e.velocityX > 0; | |
const swipeLeft = e.velocityX < 0; | |
if (swipeRight) { | |
if (listenForOpenDrawerGesture.value && !openDrawerGestureDetected.value && e.x <= 89) { | |
console.log(`OPEN DRAWER GESTURE DETECTED!`); | |
runOnJS(setDrawerVisible)(true); | |
openDrawerGestureDetected.value = true; | |
listenForOpenDrawerGesture.value = false; | |
} else if (listenForOpenDrawerGesture.value && !openDrawerGestureDetected.value && e.x > 89) { | |
console.log(`Open Drawer Gesture NOT Detected :(`); | |
openDrawerGestureDetected.value = false; | |
listenForOpenDrawerGesture.value = false; | |
} else { | |
console.log(`ELSE: listenForOpenDrawerGesture.value: ${listenForOpenDrawerGesture.value} openDrawerGestureDetected.value: ${openDrawerGestureDetected.value} e.x: ${e.x}`); | |
} | |
} else if (swipeLeft) { | |
if (leaveDrawerOpen.value) { | |
console.log(`***CLOSE*** DRAWER GESTURE DETECTED!`); | |
closeDrawerGestureDetected.value = true; | |
leaveDrawerOpen.value = false; | |
} | |
} | |
}) | |
.onEnd(() => { | |
const drawerHalfOpened = drawerLeftPosition.value >= closedLeftPosition + (drawerWidth / 2); | |
if (openDrawerGestureDetected.value && drawerHalfOpened) { | |
leaveDrawerOpen.value = true; | |
} else if (openDrawerGestureDetected.value && !drawerHalfOpened) { | |
leaveDrawerOpen.value = false; | |
runOnJS(setDrawerVisible)(false); | |
} else if (closeDrawerGestureDetected.value && drawerHalfOpened) { | |
leaveDrawerOpen.value = true; | |
} else if (closeDrawerGestureDetected.value && !drawerHalfOpened) { | |
leaveDrawerOpen.value = false; | |
runOnJS(setDrawerVisible)(false); | |
} | |
panGestureXPosition.value = -1; | |
listenForOpenDrawerGesture.value = true; | |
openDrawerGestureDetected.value = false; | |
closeDrawerGestureDetected.value = false; | |
console.log(`Gesture.Pan().onEnd()`); | |
}); | |
return ( | |
<GestureDetector gesture={gesture}> | |
<SafeAreaView style={{ flex: 1, backgroundColor: 'white' }}> | |
<Image | |
source={require('./images/dashboard.png')} | |
style={{ width: windowWidth, height: windowHeight, zIndex: 1 }} | |
/> | |
<Drawer | |
drawerLeftPosition={drawerLeftPosition} | |
/> | |
<DrawerBackground | |
openDrawerGestureDetected={openDrawerGestureDetected} | |
closeDrawerGestureDetected={closeDrawerGestureDetected} | |
leaveDrawerOpen={leaveDrawerOpen} | |
drawerVisible={drawerVisible} | |
/> | |
</SafeAreaView> | |
</GestureDetector> | |
); | |
} | |
export default App; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment