mirror of
https://github.com/status-im/react-native-camera.git
synced 2025-02-24 17:58:20 +00:00
362 lines
8.8 KiB
JavaScript
362 lines
8.8 KiB
JavaScript
|
import React from 'react';
|
||
|
import { StyleSheet, Text, View, TouchableOpacity, Slider } from 'react-native';
|
||
|
import { RNCamera } from 'react-native-camera';
|
||
|
|
||
|
const landmarkSize = 2;
|
||
|
|
||
|
const flashModeOrder = {
|
||
|
off: 'on',
|
||
|
on: 'auto',
|
||
|
auto: 'torch',
|
||
|
torch: 'off',
|
||
|
};
|
||
|
|
||
|
const wbOrder = {
|
||
|
auto: 'sunny',
|
||
|
sunny: 'cloudy',
|
||
|
cloudy: 'shadow',
|
||
|
shadow: 'fluorescent',
|
||
|
fluorescent: 'incandescent',
|
||
|
incandescent: 'auto',
|
||
|
};
|
||
|
|
||
|
export default class CameraScreen extends React.Component {
|
||
|
state = {
|
||
|
flash: 'off',
|
||
|
zoom: 0,
|
||
|
autoFocus: 'on',
|
||
|
depth: 0,
|
||
|
type: 'back',
|
||
|
whiteBalance: 'auto',
|
||
|
ratio: '16:9',
|
||
|
ratios: [],
|
||
|
photoId: 1,
|
||
|
showGallery: false,
|
||
|
photos: [],
|
||
|
faces: [],
|
||
|
};
|
||
|
|
||
|
getRatios = async function() {
|
||
|
const ratios = await this.camera.getSupportedRatios();
|
||
|
return ratios;
|
||
|
};
|
||
|
|
||
|
toggleView() {
|
||
|
this.setState({
|
||
|
showGallery: !this.state.showGallery,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
toggleFacing() {
|
||
|
this.setState({
|
||
|
type: this.state.type === 'back' ? 'front' : 'back',
|
||
|
});
|
||
|
}
|
||
|
|
||
|
toggleFlash() {
|
||
|
this.setState({
|
||
|
flash: flashModeOrder[this.state.flash],
|
||
|
});
|
||
|
}
|
||
|
|
||
|
setRatio(ratio) {
|
||
|
this.setState({
|
||
|
ratio,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
toggleWB() {
|
||
|
this.setState({
|
||
|
whiteBalance: wbOrder[this.state.whiteBalance],
|
||
|
});
|
||
|
}
|
||
|
|
||
|
toggleFocus() {
|
||
|
this.setState({
|
||
|
autoFocus: this.state.autoFocus === 'on' ? 'off' : 'on',
|
||
|
});
|
||
|
}
|
||
|
|
||
|
zoomOut() {
|
||
|
this.setState({
|
||
|
zoom: this.state.zoom - 0.1 < 0 ? 0 : this.state.zoom - 0.1,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
zoomIn() {
|
||
|
this.setState({
|
||
|
zoom: this.state.zoom + 0.1 > 1 ? 1 : this.state.zoom + 0.1,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
setFocusDepth(depth) {
|
||
|
this.setState({
|
||
|
depth,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
takePicture = async function() {
|
||
|
if (this.camera) {
|
||
|
this.camera.takePictureAsync().then(data => {
|
||
|
console.log('data: ', data);
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
onFacesDetected = ({ faces }) => this.setState({ faces });
|
||
|
onFaceDetectionError = state => console.warn('Faces detection error:', state);
|
||
|
|
||
|
renderFace({ bounds, faceID, rollAngle, yawAngle }) {
|
||
|
return (
|
||
|
<View
|
||
|
key={faceID}
|
||
|
transform={[
|
||
|
{ perspective: 600 },
|
||
|
{ rotateZ: `${rollAngle.toFixed(0)}deg` },
|
||
|
{ rotateY: `${yawAngle.toFixed(0)}deg` },
|
||
|
]}
|
||
|
style={[
|
||
|
styles.face,
|
||
|
{
|
||
|
...bounds.size,
|
||
|
left: bounds.origin.x,
|
||
|
top: bounds.origin.y,
|
||
|
},
|
||
|
]}
|
||
|
>
|
||
|
<Text style={styles.faceText}>ID: {faceID}</Text>
|
||
|
<Text style={styles.faceText}>rollAngle: {rollAngle.toFixed(0)}</Text>
|
||
|
<Text style={styles.faceText}>yawAngle: {yawAngle.toFixed(0)}</Text>
|
||
|
</View>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
renderLandmarksOfFace(face) {
|
||
|
const renderLandmark = position =>
|
||
|
position && (
|
||
|
<View
|
||
|
style={[
|
||
|
styles.landmark,
|
||
|
{
|
||
|
left: position.x - landmarkSize / 2,
|
||
|
top: position.y - landmarkSize / 2,
|
||
|
},
|
||
|
]}
|
||
|
/>
|
||
|
);
|
||
|
return (
|
||
|
<View key={`landmarks-${face.faceID}`}>
|
||
|
{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)}
|
||
|
</View>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
renderFaces() {
|
||
|
return (
|
||
|
<View style={styles.facesContainer} pointerEvents="none">
|
||
|
{this.state.faces.map(this.renderFace)}
|
||
|
</View>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
renderLandmarks() {
|
||
|
return (
|
||
|
<View style={styles.facesContainer} pointerEvents="none">
|
||
|
{this.state.faces.map(this.renderLandmarksOfFace)}
|
||
|
</View>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
renderCamera() {
|
||
|
return (
|
||
|
<RNCamera
|
||
|
ref={ref => {
|
||
|
this.camera = ref;
|
||
|
}}
|
||
|
style={{
|
||
|
flex: 1,
|
||
|
}}
|
||
|
type={this.state.type}
|
||
|
flashMode={this.state.flash}
|
||
|
autoFocus={this.state.autoFocus}
|
||
|
zoom={this.state.zoom}
|
||
|
whiteBalance={this.state.whiteBalance}
|
||
|
ratio={this.state.ratio}
|
||
|
faceDetectionLandmarks={RNCamera.Constants.FaceDetection.Landmarks.all}
|
||
|
onFacesDetected={this.onFacesDetected}
|
||
|
onFaceDetectionError={this.onFaceDetectionError}
|
||
|
focusDepth={this.state.depth}
|
||
|
>
|
||
|
<View
|
||
|
style={{
|
||
|
flex: 0.5,
|
||
|
backgroundColor: 'transparent',
|
||
|
flexDirection: 'row',
|
||
|
justifyContent: 'space-around',
|
||
|
}}
|
||
|
>
|
||
|
<TouchableOpacity style={styles.flipButton} onPress={this.toggleFacing.bind(this)}>
|
||
|
<Text style={styles.flipText}> FLIP </Text>
|
||
|
</TouchableOpacity>
|
||
|
<TouchableOpacity style={styles.flipButton} onPress={this.toggleFlash.bind(this)}>
|
||
|
<Text style={styles.flipText}> FLASH: {this.state.flash} </Text>
|
||
|
</TouchableOpacity>
|
||
|
<TouchableOpacity style={styles.flipButton} onPress={this.toggleWB.bind(this)}>
|
||
|
<Text style={styles.flipText}> WB: {this.state.whiteBalance} </Text>
|
||
|
</TouchableOpacity>
|
||
|
</View>
|
||
|
<View
|
||
|
style={{
|
||
|
flex: 0.4,
|
||
|
backgroundColor: 'transparent',
|
||
|
flexDirection: 'row',
|
||
|
alignSelf: 'flex-end',
|
||
|
}}
|
||
|
>
|
||
|
<Slider
|
||
|
style={{ width: 150, marginTop: 15, alignSelf: 'flex-end' }}
|
||
|
onValueChange={this.setFocusDepth.bind(this)}
|
||
|
step={0.1}
|
||
|
disabled={this.state.autoFocus === 'on'}
|
||
|
/>
|
||
|
</View>
|
||
|
<View
|
||
|
style={{
|
||
|
flex: 0.1,
|
||
|
backgroundColor: 'transparent',
|
||
|
flexDirection: 'row',
|
||
|
alignSelf: 'flex-end',
|
||
|
}}
|
||
|
>
|
||
|
<TouchableOpacity
|
||
|
style={[styles.flipButton, { flex: 0.1, alignSelf: 'flex-end' }]}
|
||
|
onPress={this.zoomIn.bind(this)}
|
||
|
>
|
||
|
<Text style={styles.flipText}> + </Text>
|
||
|
</TouchableOpacity>
|
||
|
<TouchableOpacity
|
||
|
style={[styles.flipButton, { flex: 0.1, alignSelf: 'flex-end' }]}
|
||
|
onPress={this.zoomOut.bind(this)}
|
||
|
>
|
||
|
<Text style={styles.flipText}> - </Text>
|
||
|
</TouchableOpacity>
|
||
|
<TouchableOpacity
|
||
|
style={[styles.flipButton, { flex: 0.25, alignSelf: 'flex-end' }]}
|
||
|
onPress={this.toggleFocus.bind(this)}
|
||
|
>
|
||
|
<Text style={styles.flipText}> AF : {this.state.autoFocus} </Text>
|
||
|
</TouchableOpacity>
|
||
|
<TouchableOpacity
|
||
|
style={[styles.flipButton, styles.picButton, { flex: 0.3, alignSelf: 'flex-end' }]}
|
||
|
onPress={this.takePicture.bind(this)}
|
||
|
>
|
||
|
<Text style={styles.flipText}> SNAP </Text>
|
||
|
</TouchableOpacity>
|
||
|
<TouchableOpacity
|
||
|
style={[styles.flipButton, styles.galleryButton, { flex: 0.25, alignSelf: 'flex-end' }]}
|
||
|
onPress={this.toggleView.bind(this)}
|
||
|
>
|
||
|
<Text style={styles.flipText}> Gallery </Text>
|
||
|
</TouchableOpacity>
|
||
|
</View>
|
||
|
{this.renderFaces()}
|
||
|
{this.renderLandmarks()}
|
||
|
</RNCamera>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
render() {
|
||
|
return <View style={styles.container}>{this.renderCamera()}</View>;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const styles = StyleSheet.create({
|
||
|
container: {
|
||
|
flex: 1,
|
||
|
paddingTop: 10,
|
||
|
backgroundColor: '#000',
|
||
|
},
|
||
|
navigation: {
|
||
|
flex: 1,
|
||
|
},
|
||
|
gallery: {
|
||
|
flex: 1,
|
||
|
flexDirection: 'row',
|
||
|
flexWrap: 'wrap',
|
||
|
},
|
||
|
flipButton: {
|
||
|
flex: 0.3,
|
||
|
height: 40,
|
||
|
marginHorizontal: 2,
|
||
|
marginBottom: 10,
|
||
|
marginTop: 20,
|
||
|
borderRadius: 8,
|
||
|
borderColor: 'white',
|
||
|
borderWidth: 1,
|
||
|
padding: 5,
|
||
|
alignItems: 'center',
|
||
|
justifyContent: 'center',
|
||
|
},
|
||
|
flipText: {
|
||
|
color: 'white',
|
||
|
fontSize: 15,
|
||
|
},
|
||
|
item: {
|
||
|
margin: 4,
|
||
|
backgroundColor: 'indianred',
|
||
|
height: 35,
|
||
|
width: 80,
|
||
|
borderRadius: 5,
|
||
|
alignItems: 'center',
|
||
|
justifyContent: 'center',
|
||
|
},
|
||
|
picButton: {
|
||
|
backgroundColor: 'darkseagreen',
|
||
|
},
|
||
|
galleryButton: {
|
||
|
backgroundColor: 'indianred',
|
||
|
},
|
||
|
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',
|
||
|
},
|
||
|
row: {
|
||
|
flexDirection: 'row',
|
||
|
},
|
||
|
});
|