How to snap pictures using expo react native camera?
You can do this in a functional component as well using a ref created via a React Hook. Here is an example based on the expo SDK 38 Camera component https://docs.expo.io/versions/v38.0.0/sdk/camera/
import React, { useState, useEffect, useRef } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { Camera } from 'expo-camera';
export default function App() {
const [hasPermission, setHasPermission] = useState(null);
const [type, setType] = useState(Camera.Constants.Type.back);
const ref = useRef(null)
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
_takePhoto = async () => {
const photo = await ref.current.takePictureAsync()
console.debug(photo)
}
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View style={{ flex: 1 }}>
<Camera style={{ flex: 1 }} type={type} ref={ref}>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}>
<TouchableOpacity
style={{
flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => {
setType(
type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
}}>
<Text style={{ fontSize: 18, marginBottom: 10, color: 'white' }}> Flip </Text>
</TouchableOpacity>
<TouchableOpacity
onPress={_takePhoto}
>
<Text>Snap Photo</Text>
</TouchableOpacity>
</View>
</Camera>
</View>
);
}
I did not check what the UI for that looks like but the main point is to utilize React.useRef
and attach the ref to your <Camera/>
component. Then you may call ref.current.takePictureAsync
to capture and access a new image. Look below to see the important relevant snippets for capturing a photo.
import React from 'react'
/* ... other imports
*/
export default CameraScene = () => {
/* ... other state and permission logic
*/
const ref = useRef(null)
const _takePhoto = async () => {
const photo = await ref.current.takePictureAsync()
console.debug(photo)
}
return (
<Camera style={{flex: 1}} ref={ref}> /* ...
... other ui logic
*/
</Camera>
)
}
Learn more about useRef
here https://reactjs.org/docs/hooks-reference.html#useref)
You'll need to add a ref to the Camera class to be able to call it's takePictureAsync function within your own 'handle' method.
cameraRef = React.createRef();
<Camera ref={this.cameraRef}>...</Camera>
Don't forget ".current" when calling the method of the referenced camera.
handlePhoto = async () => {
if(this.cameraRef){
let photo = await this.cameraRef.current.takePictureAsync();
console.log(photo);
}
}
Then simply call your 'handle' method on a touchable element acting as the photo-snap button.
<TouchableOpacity
style={{width:60, height:60, borderRadius:30, backgroundColor:"#fff"}}
onPress={this.handlePhoto} />
You should be able to see the photo logged in your console.
So you need to tell your 'circle icon' to take the picture. First I would add a reference to your camera like so
<Camera style={{ flex: 1 }}
ref={ (ref) => {this.camera = ref} }
type={this.state.type}>
then create a function that actually tells your app to take the photo:
async snapPhoto() {
console.log('Button Pressed');
if (this.camera) {
console.log('Taking photo');
const options = { quality: 1, base64: true, fixOrientation: true,
exif: true};
await this.camera.takePictureAsync(options).then(photo => {
photo.exif.Orientation = 1;
console.log(photo);
});
}
}
Now make your icon have an onPress() to take the photo. I did something like this.
<TouchableOpacity style={styles.captureButton} onPress={this.snapPhoto.bind(this)}>
<Image style={{width: 100, height: 100}} source={require('../assets/capture.png')}
/>
</TouchableOpacity>
You may also want to create a view that renders an image preview or something similar. The Expo documentation has a fairly good example on getting started. Note that Expo creates a cached folder called 'Camera' and that's where the image initially is.
You can use "onPictureSaved" when the asynchronous takePictureAsync function returns so that you can grab the photo object:
takePicture = () => {
if (this.camera) {
this.camera.takePictureAsync({ onPictureSaved: this.onPictureSaved });
}
};
onPictureSaved = photo => {
console.log(photo);
}
In the view you would have a Camera component that has a ref:
<Camera style={styles.camera} type={this.state.type} ref={(ref) => { this.camera = ref }} >
As well as a button that will call the takePicture function on press:
<TouchableOpacity style={styles.captureButton} onPress={this.takePicture} />