前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从0到1打造一款react-native App(三)Camera

从0到1打造一款react-native App(三)Camera

作者头像
j_bleach
发布2019-07-02 11:49:52
1.6K0
发布2019-07-02 11:49:52
举报
文章被收录于专栏:前端加油站前端加油站

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/j_bleach/article/details/80723293

关联文章

从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)。

组件二次封装:

代码语言:javascript
复制
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

代码语言:javascript
复制
async takePicture() {
        const options = {};
        const { path: currentImage } = await this.camera.capture({ metadata: options });
        this.setState({ currentImage });
    }

隐藏组建,返回调用界面

代码语言:javascript
复制
 back() {
        this.setState({ currentImage: null, hidden: true });
    }

拍照完成后预览照片及确认存储

代码语言:javascript
复制
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

拍照完成后预览照片及放弃存储

代码语言:javascript
复制
cancel() {
        deleteFile(this.state.currentImage);
        this.setState({ currentImage: null });
    }

照片回显

代码语言:javascript
复制
<View style={styles.container}>
      <Image style={styles.photo}
           source={{ uri: `file://${files[0].path}` }} //显示第一张照片
      />              
</View >

在照片回显时,检测文件夹,读取照片

代码语言:javascript
复制
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的文章中更新,也欢迎正在学习的同学一起交流~

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年06月18日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 关联文章
  • 拍照(摄像)需求
    • react-native-camera
      • 照片回显
        • 二维码扫描
        相关产品与服务
        对象存储
        对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档