/* eslint-disable no-console */ import React, {useState, useReducer, useRef} from 'react'; import { StyleSheet, Text, View, TouchableOpacity, Slider, TouchableWithoutFeedback, Dimensions, } from 'react-native'; // eslint-disable-next-line import/no-unresolved import { RNCamera } from 'react-native-camera'; const flashModeOrder = { off: 'on', on: 'auto', auto: 'torch', torch: 'off', }; const wbOrder = { auto: 'sunny', sunny: 'cloudy', cloudy: 'shadow', shadow: 'fluorescent', fluorescent: 'incandescent', incandescent: 'auto', }; const landmarkSize = 2; const stateReducer = (state, action) => { switch (action.type) { case 'toggleFlash': return { state.flash === flashModeOrder[ state.flash] } break; case 'toggleZoom': return { state.flash === flashModeOrder[ state.flash] } break; default: break; } } const initialState = { flash: 'off', zoom: 0, autoFocus: 'on', depth: 0, type: 'back', whiteBalance: 'auto', ratio: '16:9', isRecording: false, canDetectFaces: false, canDetectText: false, canDetectBarcode: false, faces: [], textBlocks: [], barcodes: [], recordOptions: { mute: false, maxDuration: 5, quality: RNCamera.Constants.VideoQuality['288p'], }, autoFocusPoint: { normalized: { x: 0.5, y: 0.5 }, // normalized values required for autoFocusPointOfInterest drawRectPosition: { x: Dimensions.get('window').width * 0.5 - 32, y: Dimensions.get('window').height * 0.5 - 32, }, }, }; const CameraScreen = () => { const [state, setState] = useState(initialState); const [recordOptions, togglerecordOptions] = useReducer(reducer, initialState.recordOptions); const [autoFocusPoint, touchToFocus] = useReducer(reducer, initialState.autoFocusPoint); const cameraRef = useRef(null); toggleFacing =() =>{ setState({ ...state, type: type === 'back' ? 'front' : 'back', }) } toggleFlash =() => { setState({ ...state, flash: flashModeOrder[flash] }) } toggleWB =() => { setState({ ...state, whiteBalance: wbOrder[whiteBalance] }) } toggleFocus =() =>{ setState({ ...state, autoFocus: autoFocus === 'on' ? 'off' : 'on' }) } touchToFocus =(event) => { const { pageX, pageY } = event.nativeEvent; const screenWidth = Dimensions.get('window').width; const screenHeight = Dimensions.get('window').height; const isPortrait = screenHeight > screenWidth; let x = pageX / screenWidth; let y = pageY / screenHeight; // Coordinate transform for portrait. See autoFocusPointOfInterest in docs for more info if (isPortrait) { x = pageY / screenHeight; y = -(pageX / screenWidth) + 1; } return { autoFocusPoint: { normalized: { x, y }, drawRectPosition: { x: pageX, y: pageY }, }, }; } zoomOut =() =>{ setState({ ...state, zoom: state.zoom - 0.1 < 0 ? 0 : state.zoom - 0.1, }) } zoomIn =() => { setState({ ...state, zoom: state.zoom + 0.1 > 1 ? 1 : state.zoom + 0.1, }); } setFocusDepth = (depth) => { setState({ ...state, depth: depth }); } takePicture = async function() { if (cameraRef) { const data = await cameraRef.takePictureAsync(); console.warn('takePicture ', data); } }; takeVideo = async function() { if (cameraRef) { try { const promise = cameraRef.recordAsync( state.recordOptions); if (promise) { setState({ ...state, isRecording: true }); const data = await promise; setState({ ...state, isRecording: false }); console.warn('takeVideo', data); } } catch (e) { console.error(e); } } }; toggle = value => () => { setState({ ...state, [value]: !prevState[value] }) }; facesDetected = ({ faces }) => { setState({ ...state, faces: faces }) }; renderFace = ({ bounds, faceID, rollAngle, yawAngle }) => ( ID: {faceID} rollAngle: {rollAngle.toFixed(0)} yawAngle: {yawAngle.toFixed(0)} ); renderLandmarksOfFace =(face) => { const renderLandmark = position => position && ( ); return ( {renderLandmark(face.leftEyePosition)} {renderLandmark(face.rightEyePosition)} {renderLandmark(face.leftEarPosition)} {renderLandmark(face.rightEarPosition)} {renderLandmark(face.leftCheekPosition)} {renderLandmark(face.rightCheekPosition)} {renderLandmark(face.leftMouthPosition)} {renderLandmark(face.mouthPosition)} {renderLandmark(face.rightMouthPosition)} {renderLandmark(face.noseBasePosition)} {renderLandmark(face.bottomMouthPosition)} ); } renderFaces = () => ( { state.faces.map(() => renderFace())} ); renderLandmarks = () => ( { state.faces.map(() => renderLandmarksOfFace())} ); renderTextBlocks = () => ( { state.textBlocks.map(() => renderTextBlock())} ); renderTextBlock = ({ bounds, value }) => ( {value} ); textRecognized = object => { const { textBlocks } = object; setState({ ...state, textBlocks: textBlocks }); }; barcodeRecognized = ({ barcodes }) => { setState({ ...stat, barcodes }) }; renderBarcodes = () => ( { state.barcodes.map( renderBarcode)} ); renderBarcode = ({ bounds, data, type }) => ( {`${data} ${type}`} ); renderCamera = () => { const { canDetectFaces, canDetectText, canDetectBarcode } = state; const drawFocusRingPosition = { top: state.autoFocusPoint.drawRectPosition.y - 32, left: state.autoFocusPoint.drawRectPosition.x - 32, }; return ( { cameraRef = ref; }} style={{ flex: 1, justifyContent: 'space-between', }} type={ state.type} flashMode={ state.flash} autoFocus={ state.autoFocus} autoFocusPointOfInterest={ state.autoFocusPoint.normalized} zoom={ state.zoom} whiteBalance={ state.whiteBalance} ratio={ state.ratio} focusDepth={ state.depth} androidCameraPermissionOptions={{ title: 'Permission to use camera', message: 'We need your permission to use your camera', buttonPositive: 'Ok', buttonNegative: 'Cancel', }} faceDetectionLandmarks={ RNCamera.Constants.FaceDetection.Landmarks ? RNCamera.Constants.FaceDetection.Landmarks.all : undefined } onFacesDetected={canDetectFaces ? facesDetected() : null} onTextRecognized={canDetectText ? textRecognized() : null} onGoogleVisionBarcodesDetected={canDetectBarcode ? barcodeRecognized() : null} > touchToFocus()}> toggleFacing()}> FLIP toggleFlash()}> FLASH: { state.flash} toggleWB()}> WB: { state.whiteBalance} toggle('canDetectFaces')} style={styles.flipButton}> {!canDetectFaces ? 'Detect Faces' : 'Detecting Faces'} toggle('canDetectText')} style={styles.flipButton}> {!canDetectText ? 'Detect Text' : 'Detecting Text'} toggle('canDetectBarcode')} style={styles.flipButton}> {!canDetectBarcode ? 'Detect Barcode' : 'Detecting Barcode'} setFocusDepth()} step={0.1} disabled={ state.autoFocus === 'on'} /> {} : () => takeVideo()} > { state.isRecording ? ( ) : ( REC )} { state.zoom !== 0 && ( Zoom: { state.zoom} )} zoomIn()} > + zoomOut()} > - toggleFocus()} > AF : { state.autoFocus} takePicture()} > SNAP {!!canDetectFaces && renderFaces()} {!!canDetectFaces && renderLandmarks()} {!!canDetectText && renderTextBlocks()} {!!canDetectBarcode && renderBarcodes()} ); } return ( {() => renderCamera()} ); } export default CameraScreen; const styles = StyleSheet.create({ container: { flex: 1, paddingTop: 10, backgroundColor: '#000', }, flipButton: { flex: 0.3, height: 40, marginHorizontal: 2, marginBottom: 10, marginTop: 10, borderRadius: 8, borderColor: 'white', borderWidth: 1, padding: 5, alignItems: 'center', justifyContent: 'center', }, autoFocusBox: { position: 'absolute', height: 64, width: 64, borderRadius: 12, borderWidth: 2, borderColor: 'white', opacity: 0.4, }, flipText: { color: 'white', fontSize: 15, }, zoomText: { position: 'absolute', bottom: 70, zIndex: 2, left: 2, }, picButton: { backgroundColor: 'darkseagreen', }, facesContainer: { position: 'absolute', bottom: 0, right: 0, left: 0, top: 0, }, face: { padding: 10, borderWidth: 2, borderRadius: 2, position: 'absolute', borderColor: '#FFD700', justifyContent: 'center', backgroundColor: 'rgba(0, 0, 0, 0.5)', }, landmark: { width: landmarkSize, height: landmarkSize, position: 'absolute', backgroundColor: 'red', }, faceText: { color: '#FFD700', fontWeight: 'bold', textAlign: 'center', margin: 10, backgroundColor: 'transparent', }, text: { padding: 10, borderWidth: 2, borderRadius: 2, position: 'absolute', borderColor: '#F00', justifyContent: 'center', }, textBlock: { color: '#F00', position: 'absolute', textAlign: 'center', backgroundColor: 'transparent', }, });