从0到1打造一款react-native App(三)Camera
关联文章
从0到1打造一款react-native App(一)环境配置
从0到1打造一款react-native App(二)Navigation+Redux
项目地址:https://github.com/jiwenjiang/react-native-nfc
拍照(摄像)需求
拍照的主要需求是在拍照后,不将照片在系统相册中显示出来,android拍照后会默认存储在DCIM文件夹当中,而这次主要需要做的就是把照片放在自定义的文件夹当中。
react-native-camera
拍照的第三方包有很多,比如react-native-image-picker,这个调用的是系统相机,用法比较简单,但是拓展性较差,不管是这次项目主要的需求(拍照后不在系统相册显示),还是本身拍照时的一些定制化的需求,类似微信拍照那种,都不容易实现,因此选择了react-native-camera。
最新版的react-native-camera(v 1.1.x)已经支持了人脸识别,文字识别等功能,还是很强大的,这些功能可能日后都会用得到,不过因为一些版本和平台的原因之后会换成expo的camera,这里暂时还是介绍rn的camera(v 0.7)。
组件二次封装:
import React, { Component } from 'react';
import {
Dimensions,
StyleSheet,
Button,
Text,
ImageBackground,
View,
TouchableOpacity
} from 'react-native';
import Camera from 'react-native-camera';
import Icon from 'react-native-vector-icons/MaterialIcons';
import { deleteFile, mkdir, readPath } from '../../service/utils/fileOperations';
import RNFS from 'react-native-fs';
import moment from 'moment/moment';
class RNCamera extends Component {
constructor(props) {
super(props);
this.state = {
hidden: false,
currentImage: null
};
}
async takePicture() {
const options = {};
const { path: currentImage } = await this.camera.capture({ metadata: options });
this.setState({ currentImage });
}
back() {
this.setState({ currentImage: null, hidden: true });
}
async check() {
const [date, unixTime] = [moment().format('YYYY/MM/DD'), moment().unix()];
const dir = `${RNFS.DocumentDirectoryPath}/photo/${date}`;
await mkdir(dir);
const url = `${dir}/${unixTime}.jpg`;
await RNFS.moveFile(this.state.currentImage, url);
console.log(await readPath(dir));
this.setState({ currentImage: null });
}
cancel() {
deleteFile(this.state.currentImage);
this.setState({ currentImage: null });
}
render() {
const { currentImage, hidden } = this.state;
return (
<View style={[styles.container, hidden && styles.hidden]}>
{currentImage ? <ImageBackground style={styles.photo} source={{ uri: currentImage }}>
<TouchableOpacity style={styles.capture} onPress={() => this.cancel()}>
<Icon name="close" size={30}/>
</TouchableOpacity >
<TouchableOpacity style={styles.capture} onPress={() => this.check()}>
<Icon name="check" size={30}/>
</TouchableOpacity >
</ImageBackground >
: <Camera ref={(cam) => {
this.camera = cam;
}}
style={styles.preview}
aspect={Camera.constants.Aspect.fill}
captureTarget={Camera.constants.CaptureTarget.temp}
>
<TouchableOpacity style={styles.capture} onPress={() => this.back()}>
<Icon name="expand-more" size={30}/>
</TouchableOpacity >
<TouchableOpacity style={styles.capture} onPress={() => this.takePicture()}>
<Icon name="camera-alt" size={30}/>
</TouchableOpacity >
</Camera >
}
</View >
);
}
}
const styles = StyleSheet.create(
{
container: {
flex: 1,
flexDirection: 'row'
},
preview: {
flex: 1,
justifyContent: 'center',
flexDirection: 'row',
alignItems: 'flex-end'
},
capture: {
flex: 0,
backgroundColor: 'rgba(255, 255, 255, 0.3)',
borderRadius: 25,
margin: 20,
marginBottom: 30,
width: 50,
height: 50,
alignItems: 'center',
justifyContent: 'center',
zIndex: 1
},
photo: {
flex: 1,
justifyContent: 'center',
flexDirection: 'row',
alignItems: 'flex-end'
},
hidden: {
display: 'none'
}
}
);
export default RNCamera;
没有对react-native-camera做过多的配置,需要注意的配置是captureTarget属性。在v0.7版本的camera当中,captureTarget的可选配置项有4种。
- Camera.constants.CaptureTarget.cameraRoll(默认,存储在系统相册中)
- Camera.constants.CaptureTarget.disk(存储在磁盘中,这是官方推荐的存储方式,会提升拍照的响应速度)
- Camera.constants.CaptureTarget.temp (存储在临时文件夹,当前项目选择方案)
-
Camera.constants.CaptureTarget.memory (以base64的形式存储在内存当中,这个选项在之后的版本已经被废弃了,不过0.7版本还是可以用的)
实现基本思路是,通过外层调用来控制整个组件的样式值,来管理组件的显示与隐藏,即组件state的hidden属性。当组件被成功调用显示时,组件主要分为两块,拍照和预览。给定一个拍照照片的路径值,即组件state的currentImage,如果当前组件存在一个照片的存储路径,就显示预览界面,如不存在就显示拍照界面。而currentImage的值通过拍照成功的Promise或者取消的状态去控制创建与删除。
拍照时去创建currentImage
async takePicture() {
const options = {};
const { path: currentImage } = await this.camera.capture({ metadata: options });
this.setState({ currentImage });
}
隐藏组建,返回调用界面
back() {
this.setState({ currentImage: null, hidden: true });
}
拍照完成后预览照片及确认存储
async check() {
const [date, unixTime] = [moment().format('YYYY/MM/DD'), moment().unix()];
const dir = `${RNFS.DocumentDirectoryPath}/photo/${date}`;
await mkdir(dir);
const url = `${dir}/${unixTime}.jpg`;
await RNFS.moveFile(this.state.currentImage, url);
console.log(await readPath(dir));
this.setState({ currentImage: null });
}
存储这里用到了react-native-fs,这个第三方包就不过多介绍了,都是一些基础的文件操作,比较好理解。通过在文件路径下新建photo/xxxx-xx-xx的文件夹,确保每天拍摄的照片存放在当日的文件夹,方便后续的文件预览时的筛选。在照片拍摄完毕后,react-native-camera会将拍摄的照片存放至临时文件夹,而这里需要做的就是将临时文件夹的照片移动至我们的目标文件夹,这里顺便说一下,文件move操作的性能是优于read+write的,这里切记用move。关于android文件存储这里推荐一篇介绍的比较详细的文章https://juejin.im/post/58b557de128fe10065e93cc8。
拍照完成后预览照片及放弃存储
cancel() {
deleteFile(this.state.currentImage);
this.setState({ currentImage: null });
}
操作预览:
照片回显
<View style={styles.container}>
<Image style={styles.photo}
source={{ uri: `file://${files[0].path}` }} //显示第一张照片
/>
</View >
在照片回显时,检测文件夹,读取照片
const mkdir = async (url) => {
const dirExists = await RNFS.exists(url);
if (dirExists) {
return new Promise(resolve => resolve(dirExists));
}
await RNFS.mkdir(url);
return new Promise(resolve => resolve(url));
};
async function storageFile() {
const date = moment().format('YYYY/MM/DD');
const url = `${RNFS.DocumentDirectoryPath}/photo/${date}`;
await mkdir(url);
const files = await readPath(url);
return files;
}
二维码扫描
react-native-camera支持对各种条形码的扫描识别,主要的属性有两个
barCodeTypes={[Camera.constants.BarCodeType.qr]} //扫码的类型
onBarCodeRead={this.props.onScanResultReceived} //扫码成功后的回调
项目这里直接把https://www.jianshu.com/p/347ccf787d62这篇文章中二次封装好的一个二维码扫描的组件复制了过来。主要是视图层的二次封装,有兴趣的同学也可以自己封装。
之后会把react-native-camera替换成expo中的camera,换完之后会继续在这篇camera的文章中更新,也欢迎正在学习的同学一起交流~
上一篇: 在Android中使用WebSocket实现消息通信的方法详解
下一篇: THINK PHP5 学习