How to snap pictures using expo react native camera? - react-native

I have just started using React Native with Expo so I am kind of confused. So, I have made a camera component which I imported in the main screen. Everything looks good. But I can't take pictures. I cannot click the snap icon and save the image. Is there a component that I missed?
I have only posted the CameraComponent class below.
Camera.js
class CameraComponent extends Component {
state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back
}
async componentWillMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === 'granted' })
}
render() {
const { hasCameraPermission } = this.state
if (hasCameraPermission === null) {
return <View />
}
else if (hasCameraPermission === false) {
return <Text> No access to camera</Text>
}
else {
return (
<View style={{ flex: 1 }}>
<Camera
style={{ flex: 1, justifyContent: 'space-between' }}
type={this.state.type}
>
<Header
searchBar
rounded
style={{
position: 'absolute',
backgroundColor: 'transparent',
left: 0,
top: 0,
right: 0,
zIndex: 100,
alignItems: 'center'
}}
>
<View style={{ flexDirection: 'row', flex: 4 }}>
<Ionicons name="md-camera" style={{ color: 'white' }} />
<Item style={{ backgroundColor: 'transparent' }}>
<Icon name="ios-search" style={{ color: 'white', fontSize: 24, fontWeight: 'bold' }}></Icon>
</Item>
</View>
<View style={{ flexDirection: 'row', flex: 2, justifyContent: 'space-around' }}>
<Icon name="ios-flash" style={{ color: 'white', fontWeight: 'bold' }} />
<Icon
onPress={() => {
this.setState({
type: this.state.type === Camera.Constants.Type.back ?
Camera.Constants.Type.front :
Camera.Constants.Type.back
})
}}
name="ios-reverse-camera"
style={{ color: 'white', fontWeight: 'bold' }}
/>
</View>
</Header>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 30, marginBottom: 15, alignItems: 'flex-end' }}>
<Ionicons name="ios-map" style={{ color: 'white', fontSize: 36 }}></Ionicons>
<View></View>
<View style={{ alignItems: 'center' }}>
<MaterialCommunityIcons name="circle-outline" // This is the icon which should take and save image
style={{ color: 'white', fontSize: 100 }}
></MaterialCommunityIcons>
<Icon name="ios-images" style={{ color: 'white', fontSize: 36 }} />
</View>
</View>
</Camera>
</View>
)
}
}
}
export default CameraComponent;
The icon in the center i;e circle icon should automatically take and save image.

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} />

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 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.

Are you trying to do this on an actual physical device? You can't shoot pictures with an emulator.

Related

Expo Camera crashing on Android devices

I'm trying to use Expo Camera to take pictures and scan barcodes, but for some reason whenever I run it on an Android device when I am about to use the camera the app crashes. This is the code used for taking pictures/scanning barcodes. I don't believe it is a code issue though:
import React, { useState, useEffect } from 'react';
import { Text, View, TouchableOpacity, Image, Alert } from 'react-native';
import { IconButton, Colors, Button } from 'react-native-paper'
import { Camera } from 'expo-camera';
const CustomBarcodeScanner = ({ handleBarCodeScanned, scanning, handleTakePhotos, handleGoBack }) => {
const [hasPermission, setHasPermission] = useState(null)
const [preview, setPreview] = useState(false)
const [currentPhoto, setCurrentPhoto] = useState('')
const [photoCount, setPhotoCount] = useState(0)
const displaySnap = scanning ? "none" : "flex"
const snap = async() => {
if(this.camera){
let photo = await this.camera.takePictureAsync({base64: true})
setCurrentPhoto(photo['base64'])
setPhotoCount(photoCount + 1)
setPreview(true)
}
}
const acceptPhoto = () => {
setPreview(false)
handleTakePhotos(currentPhoto)
setCurrentPhoto('')
console.log(photoCount)
if(photoCount >= 2){
handleGoBack()
return
}
Alert.alert(
"Tomar otra foto",
"¿Desea tomar otra foto?",
[
{
text: 'Sí',
onPress: () => {
}
},
{
text: 'No',
onPress: () => {
handleGoBack()
},
style: "cancel"
}
],
{ cancelable: false }
)
}
const retakePhoto = () => {
setPreview(false)
setCurrentPhoto('')
}
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === 'granted');
})()
}, [])
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return preview ?
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Image style={{width: '100%', height: '70%'}} source={{ uri: `data:image/jpg;base64,${currentPhoto}` }} />
<View style={{flexDirection: 'row'}}>
<Button
mode='contained'
color={Colors.purple500}
style={{padding: 10, margin: 10}}
onPress={acceptPhoto}
>
Aceptar
</Button>
<Button
mode='contained'
color={Colors.purple500}
style={{padding: 10, margin: 10}}
onPress={retakePhoto}
>
Re-Tomar Foto
</Button>
</View>
</View>
:
<View style={{ flex: 1 }}>
<Camera
style={{ flex: 1 }} type={Camera.Constants.Type.back} onBarCodeScanned={handleBarCodeScanned} ref={ref => {this.camera = ref}}>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}
>
<TouchableOpacity
style={{
flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={handleGoBack}
>
<Text style={{ fontSize: 18, marginBottom: 10, color: 'white' }}>Regresar</Text>
</TouchableOpacity>
<TouchableOpacity
style={{
flex: 0.8,
alignSelf: 'flex-end',
alignItems: 'center',
display: displaySnap
}}
onPress={() => snap() }
>
<IconButton icon='camera' background={Colors.black} size={50} color={Colors.white} />
</TouchableOpacity>
</View>
</Camera>
</View>
}
export default CustomBarcodeScanner
I'm thinking it could be dependency related? I'm not sure if I need to install some libraries or add some code to get it to work. The error I get says Error while updating property 'nativeBackgroundAndroid' of a view managed by: RCTView
My expo version is 4.1.6
Apparently this is an issue with the Touchable Element with Ripple Effect. People are discussing that here.
react-native-paper seems to implement that on their button. Try to use the default button from React-Native, that should fix it :)

How to not capture audio playback running on device when capturing new video

I am creating an app in expo/react-native where I am layering many videos captured by the user using expo-av, something like an acapella effect. Now if the user doesn't use headphones and tries to layer a new video listening to their previous recording(s), the sound of their previous recordings (playing on the phone speakers) comes as a disturbance on the new recording.
It seems to be possible to avoid this as we see when using Skype or Zoom one cannot hear feedback of their own voices on the sound coming in from the remote user.
It is possible using expo-av or any react native library, to cancel out speaker sound?
Sharing some code in case useful
import React, { useState, useEffect, useRef } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { Camera } from 'expo-camera';
import {Video} from 'expo-av';
import { Ionicons } from '#expo/vector-icons';
export default function App() {
const [hasPermission, setHasPermission] = useState(null);
const [cameraRef, setCameraRef] = useState(null);
const [videoRef,setVideoRef] = useState(null);
const [type, setType] = useState(Camera.Constants.Type.back);
const [recording, setRecording] = useState(false);
const [videoSource, setVideoSource]= useState('http://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4')
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const fireOnLoad=()=>{
}
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 => {
setCameraRef(ref) ;
}}>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
justifyContent: 'flex-end'
}}>
<View style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-evenly'
}}>
<TouchableOpacity
style={{
flex: 0.1,
alignSelf: 'flex-end'
}}
onPress={() => {
setType(
type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
}}>
<Ionicons name={ Platform.OS === 'ios' ? "ios-reverse-camera" : 'md-reverse-camera'} size={40} color="white" />
</TouchableOpacity>
<TouchableOpacity style={{alignSelf: 'center'}} onPress={async() => {
if(cameraRef){
let photo = await cameraRef.takePictureAsync();
console.log('photo', photo);
}
}}>
<View style={{
borderWidth: 2,
borderRadius:60,
borderColor: 'white',
height: 50,
width:50,
display: 'flex',
justifyContent: 'center',
alignItems: 'center'}}
>
<View style={{
borderWidth: 2,
borderRadius:60,
borderColor: 'white',
height: 40,
width:40,
backgroundColor: 'white'}} >
</View>
</View>
</TouchableOpacity>
<TouchableOpacity style={{alignSelf: 'center'}} onPress={async() => {
if(!recording){
setRecording(true)
let video = await cameraRef.recordAsync();
//console.log('video', video);
setVideoSource(video.uri);
} else {
setRecording(false)
cameraRef.stopRecording()
}
}}>
<View style={{
borderWidth: 2,
borderRadius:60,
borderColor: 'red',
height: 50,
width:50,
display: 'flex',
justifyContent: 'center',
alignItems: 'center'}}
>
<View style={{
borderWidth: 2,
borderRadius:60,
borderColor: 'red',
height: 40,
width:40,
backgroundColor: 'red'}} >
</View>
</View>
</TouchableOpacity>
</View>
</View>
</Camera>
<Video
source={{ uri: videoSource }}
style={{ flex: 1 }}
useNativeControls='true'
resizeMode={Video.RESIZE_MODE_COVER}
onLoad={fireOnLoad}
onError={fireOnLoad}
isMuted={false}
/>
</View>
);
}

wants to capture image in by using expo camera

i'm trying to capture image from expo camera but when i alert my photo nothing shown up here is complete code of my component
import React from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { Camera, Permissions } from 'expo';
import {
Container,
Title,
Content,
Header,
Button,
Switch,
Left,
Body,
Right,
List,ListItem,Thumbnail,Footer,FooterTab
} from "native-base";
import {Icon} from 'react-native-elements';
export default class CameraEx extends React.Component {
static navigationOptions = {
header: null
}
state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back,
};
async componentDidMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === 'granted' });
}
takePicture = async function() {
if (this.camera) {
let photo = await this.camera.takePictureAsync();
alert(photo);
}
}
render() {
const { hasCameraPermission } = this.state;
if (hasCameraPermission === null) {
return <View />;
} else if (hasCameraPermission === false) {
return <Text>No access to camera</Text>;
} else {
return (
<Container style={{ flex: 1 }}>
<Header transparent>
<Left>
<Button transparent onPress={() =>this.props.navigation.navigate('Home') }>
<Icon name='arrow-back' />
</Button>
</Left>
</Header>
<Camera style={{ flex: 1 }} type={this.state.type}>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
justifyContent:'space-between',
width:"50%"
}}>
<TouchableOpacity
style={{
//flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => {
this.setState({
type: this.state.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
style={{
// flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={()=>this.takePicture()}
>
<Text
style={{ fontSize: 18, marginBottom: 10, color: 'white' }}>
{' '}Take Picture{' '}
</Text>
</TouchableOpacity>
</View>
</Camera>
</Container>
);
}
}
}
here is a complete code my component please let me know how i can capture image and stored into state i'm beginner ............................................................................................................................................. ....
you cannot show a picture in alert box. you need to pass the source or base64 of image to react native Image component.
import {ImagePicker} from 'expo';
const options = {
base64: true,
allowsEditing: true
};
const data = await ImagePicker.launchCameraAsync(options);
if (data.cancelled !== true) {
this.setState({imageBase64: data.base64});
}
and then you can use like this:
<Image source={`data:image/png;base64, ${this.state.imageBase64}`} />

Black image captured from expo takeSnapshotAsync

I have a react native component in which I am trying to capture an image.
The problem is when I capture the image, it is returning a black image.
My component is as follows:
import React from 'react';
var inspect = require('util-inspect');
import { Text, View, TouchableOpacity, Button, Image } from 'react-native';
import { Camera, Permissions, takeSnapshotAsync } from 'expo';
export default class CameraScreen extends React.Component {
state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back,
imageUrI: ''
};
async componentWillMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === 'granted' });
}
async capture(e){
console.log('e', takeSnapshotAsync)
try{
let result = await takeSnapshotAsync(this.refs.camera, {
format: 'png',
result: 'file',
});
this.setState({imageUrI: result})
this.upload(result)
console.log("result", result)
}catch(err){
console.log('err', err)
}
}
upload(photo_uri){
const data = new FormData();
data.append('name', 'testName'); // you can append anyone.
data.append('webcam', {
uri: photo_uri,
type: 'image/png', // or photo.type
name: 'testPhotoName'
});
fetch('http://0647ad02.ngrok.io/upload', {
method: 'post',
body: data
}).then(res => {
console.log('res', res)
}).catch(err => {
console.log("err", err)
})
}
render() {
const { hasCameraPermission } = this.state;
if (hasCameraPermission === null) {
return <View />;
} else if (hasCameraPermission === false) {
return <Text>No access to camera</Text>;
} else if (this.state.imageUrI) {
return (<Image
style={{flex: 1}}
source={{uri: this.state.imageUrI}}
/>);
} else {
return (
<View
style={{
flex: 1,
flexDirection: 'column',
}}
>
<Camera
style={{ flex: 3 }}
type={this.state.type}
ref="camera"
>
</Camera>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}>
<TouchableOpacity
style={{
flex: 0.5,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => {
this.setState({
type: this.state.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
style={{
flex: 0.5,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => this.capture()}>
<Text
style={{ borderColor: 'white', borderWidth: 1, borderRadius: 100, width: 100, height: 100, fontSize: 18, marginBottom: 10, color: 'white' }}>
{' '}
</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
}
As you can see, I have a <Camera> component imported from expo. This then has a touch component to handle the capture() action.
My capture function is using this.refs.camera to target the view and then passes the options to return a file and png as a result.
I am guessing it is one of these two options that are wrong? Either I am targeting the wrong element (I have tried others to no avail) or the file returned doesn't actually exist? I have tried returning a binary file but having trouble posting that in the upload function.
Am I on the right track?
I am testing on an Android One Plus 2 phone running android 6.0.1
Thanks
Camera from Expo is currently unsupported by .takeSnapshotAsync, unfortunately. See https://github.com/expo/expo/issues/1003 for more details

Using react-native-camera, how to access saved pictures?

My goal is to use the react-native-camera and simply show a picture on the same screen, if a picture has been taken. I'm trying to save the picture source as "imageURI". If it exists, I want to show it, if a picture hasn't been taken yet, just show text saying No Image Yet. I've got the camera working, since I can trace the app is saving pictures to the disk. Having trouble with the following:
How to assign the capture functions data to a variable when I take the picture, that I can call later (imageURI).
Don't know how to do an if statement in Javascript to check if a variable exists yet.
import Camera from 'react-native-camera';
export default class camerahere extends Component {
_takePicture () {
this.camera.capture((err, data) => {
if (err) return;
imageURI = data;
});
}
render() {
if ( typeof imageURI == undefined) {
image = <Text> No Image Yet </Text>
} else {
image = <Image source={{uri: imageURI, isStatic:true}}
style={{width: 100, height: 100}} />
}
return (
<View style={styles.container}>
<Camera
captureTarget={Camera.constants.CaptureTarget.disk}
ref={(cam) => {
this.camera = cam;
}}
style={styles.preview}
aspect={Camera.constants.Aspect.fill}>
{button}
<TouchableHighlight onPress={this._takePicture.bind(this)}>
<View style={{height:50,width:50,backgroundColor:"pink"}}></View>
</TouchableHighlight>
</Camera>
I found the answer to my own question. This is an example of the react-native-camera being used.
https://github.com/spencercarli/react-native-snapchat-clone/blob/master/app/routes/Camera.js
Found this answer in another earlier posted question answered by #vinayr. Thanks!
Get recently clicked image from camera on image view in react-native
Here's the code from the first link:
import React, { Component } from 'react';
import {
View,
StyleSheet,
Dimensions,
TouchableHighlight,
Image,
Text,
} from 'react-native';
import Camera from 'react-native-camera';
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#000',
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center',
height: Dimensions.get('window').height,
width: Dimensions.get('window').width
},
capture: {
width: 70,
height: 70,
borderRadius: 35,
borderWidth: 5,
borderColor: '#FFF',
marginBottom: 15,
},
cancel: {
position: 'absolute',
right: 20,
top: 20,
backgroundColor: 'transparent',
color: '#FFF',
fontWeight: '600',
fontSize: 17,
}
});
class CameraRoute extends Component {
constructor(props) {
super(props);
this.state = {
path: null,
};
}
takePicture() {
this.camera.capture()
.then((data) => {
console.log(data);
this.setState({ path: data.path })
})
.catch(err => console.error(err));
}
renderCamera() {
return (
<Camera
ref={(cam) => {
this.camera = cam;
}}
style={styles.preview}
aspect={Camera.constants.Aspect.fill}
captureTarget={Camera.constants.CaptureTarget.disk}
>
<TouchableHighlight
style={styles.capture}
onPress={this.takePicture.bind(this)}
underlayColor="rgba(255, 255, 255, 0.5)"
>
<View />
</TouchableHighlight>
</Camera>
);
}
renderImage() {
return (
<View>
<Image
source={{ uri: this.state.path }}
style={styles.preview}
/>
<Text
style={styles.cancel}
onPress={() => this.setState({ path: null })}
>Cancel
</Text>
</View>
);
}
render() {
return (
<View style={styles.container}>
{this.state.path ? this.renderImage() : this.renderCamera()}
</View>
);
}
};
export default CameraRoute;