Created
June 25, 2021 18:23
-
-
Save amandeepmittal/8401a1b07429db386628f7c639d2cd8b to your computer and use it in GitHub Desktop.
This file contains 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, useRef, useEffect } from 'react'; | |
import { | |
StyleSheet, | |
Dimensions, | |
View, | |
Text, | |
TouchableOpacity | |
} from 'react-native'; | |
import { Camera } from 'expo-camera'; | |
import { Video } from "expo-av"; | |
import { AntDesign, MaterialIcons } from '@expo/vector-icons'; | |
const WINDOW_HEIGHT = Dimensions.get('window').height; | |
const CAPTURE_SIZE = Math.floor(WINDOW_HEIGHT * 0.08); | |
export function SnapComponent() { | |
const cameraRef = useRef(); | |
const [hasPermission, setHasPermission] = useState(null); | |
const [cameraType, setCameraType] = useState(Camera.Constants.Type.back); | |
const [isPreview, setIsPreview] = useState(false); | |
const [isCameraReady, setIsCameraReady] = useState(false); | |
const [isVideoRecording, setIsVideoRecording] = useState(false); | |
const [videoSource, setVideoSource] = useState(null); | |
useEffect(() => { | |
onHandlePermission(); | |
}, []); | |
const onHandlePermission = async () => { | |
const { status } = await Camera.requestPermissionsAsync(); | |
setHasPermission(status === 'granted'); | |
}; | |
const onCameraReady = () => { | |
setIsCameraReady(true); | |
}; | |
const switchCamera = () => { | |
if (isPreview) { | |
return; | |
} | |
setCameraType(prevCameraType => | |
prevCameraType === Camera.Constants.Type.back | |
? Camera.Constants.Type.front | |
: Camera.Constants.Type.back | |
); | |
}; | |
const onSnap = async () => { | |
if (cameraRef.current) { | |
const options = { quality: 0.7, base64: true }; | |
const data = await cameraRef.current.takePictureAsync(options); | |
const source = data.base64; | |
if (source) { | |
await cameraRef.current.pausePreview(); | |
setIsPreview(true); | |
} | |
} | |
}; | |
const recordVideo = async () => { | |
if (cameraRef.current) { | |
try { | |
const videoRecordPromise = cameraRef.current.recordAsync(); | |
if (videoRecordPromise) { | |
setIsVideoRecording(true); | |
const data = await videoRecordPromise; | |
const source = data.uri; | |
if (source) { | |
setIsPreview(true); | |
console.log("video source", source); | |
setVideoSource(source); | |
} | |
} | |
} catch (error) { | |
console.warn(error); | |
} | |
} | |
}; | |
const stopVideoRecording = () => { | |
if (cameraRef.current) { | |
setIsPreview(false); | |
setIsVideoRecording(false); | |
cameraRef.current.stopRecording(); | |
} | |
}; | |
const cancelPreview = async () => { | |
await cameraRef.current.resumePreview(); | |
setIsPreview(false); | |
setVideoSource(null); | |
}; | |
const renderVideoPlayer = () => ( | |
<Video | |
source={{ uri: videoSource }} | |
shouldPlay={true} | |
style={styles.media} | |
/> | |
); | |
const renderVideoRecordIndicator = () => ( | |
<View style={styles.recordIndicatorContainer}> | |
<View style={styles.recordDot} /> | |
<Text style={styles.recordTitle}>{"Recording..."}</Text> | |
</View> | |
); | |
if (hasPermission === null) { | |
return <View />; | |
} | |
if (hasPermission === false) { | |
return <Text style={styles.text}>No access to camera</Text>; | |
} | |
return ( | |
<View style={styles.container}> | |
<Camera | |
ref={cameraRef} | |
style={styles.container} | |
type={cameraType} | |
onCameraReady={onCameraReady} | |
/> | |
{isVideoRecording && renderVideoRecordIndicator()} | |
{videoSource && renderVideoPlayer()} | |
<View style={styles.container}> | |
{isPreview && ( | |
<TouchableOpacity onPress={cancelPreview} style={styles.closeButton}> | |
<AntDesign name='close' size={32} color='#fff' /> | |
</TouchableOpacity> | |
)} | |
{!videoSource && !isPreview && ( | |
<View style={styles.bottomButtonsContainer}> | |
<TouchableOpacity disabled={!isCameraReady} onPress={switchCamera}> | |
<MaterialIcons name='flip-camera-ios' size={28} color='white' /> | |
</TouchableOpacity> | |
<TouchableOpacity | |
activeOpacity={0.7} | |
disabled={!isCameraReady} | |
onPress={onSnap} | |
style={styles.capture} | |
onLongPress={recordVideo} | |
onPressOut={stopVideoRecording} | |
/> | |
</View> | |
)} | |
</View> | |
</View> | |
); | |
} | |
const styles = StyleSheet.create({ | |
container: { | |
...StyleSheet.absoluteFillObject | |
}, | |
text: { | |
color: '#fff' | |
}, | |
bottomButtonsContainer: { | |
position: 'absolute', | |
flexDirection: 'row', | |
bottom: 28, | |
width: '100%', | |
alignItems: 'center', | |
justifyContent: 'center' | |
}, | |
closeButton: { | |
position: 'absolute', | |
top: 35, | |
right: 20, | |
height: 50, | |
width: 50, | |
borderRadius: 25, | |
justifyContent: 'center', | |
alignItems: 'center', | |
backgroundColor: '#5A45FF', | |
opacity: 0.7 | |
}, | |
capture: { | |
backgroundColor: '#5A45FF', | |
borderRadius: 5, | |
height: CAPTURE_SIZE, | |
width: CAPTURE_SIZE, | |
borderRadius: Math.floor(CAPTURE_SIZE / 2), | |
marginBottom: 28, | |
marginHorizontal: 30, | |
borderWidth: 2, | |
borderColor: '#fff' | |
}, | |
recordIndicatorContainer: { | |
flexDirection: "row", | |
position: "absolute", | |
top: 25, | |
alignSelf: "center", | |
justifyContent: "center", | |
alignItems: "center", | |
backgroundColor: "transparent", | |
opacity: 0.7, | |
}, | |
recordTitle: { | |
fontSize: 14, | |
color: "#ffffff", | |
textAlign: "center", | |
}, | |
recordDot: { | |
borderRadius: 3, | |
height: 6, | |
width: 6, | |
backgroundColor: "#ff0000", | |
marginHorizontal: 5, | |
}, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment