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

關聯文章


從0到1打造一款react-native App(一)環境配置

從0到1打造一款react-native App(二)Navigation+Reduxreact

項目地址:https://github.com/jiwenjiang...android

拍照(攝像)需求

拍照的主要需求是在拍照後,不將照片在系統相冊中顯示出來,android拍照後會默認存儲在DCIM文件夾當中,而此次主要須要作的就是把照片放在自定義的文件夾當中。git

react-native-camera

拍照的第三方包有不少,好比react-native-image-picker,這個調用的是系統相機,用法比較簡單,可是拓展性較差,無論是此次項目主要的需求(拍照後不在系統相冊顯示),仍是自己拍照時的一些定製化的需求,相似微信拍照那種,都不容易實現,所以選擇了react-native-cameragithub

最新版的react-native-camera(v 1.1.x)已經支持了人臉識別,文字識別等功能,仍是很強大的,這些功能可能往後都會用獲得,不過由於一些版本和平臺的緣由以後會換成expo的camera,這裏暫時仍是介紹rn的camera(v 0.7)。segmentfault

組件二次封裝:react-native

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或者取消的狀態去控制建立與刪除。async

拍照時去建立currentImagepost

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/58b557...

拍照完成後預覽照片及放棄存儲

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/347... 這篇文章中二次封裝好的一個二維碼掃描的組件複製了過來。主要是視圖層的二次封裝,有興趣的同窗也能夠本身封裝。

以後會把react-native-camera替換成expo中的camera,換完以後會繼續在這篇camera的文章中更新,也歡迎正在學習的同窗一塊兒交流~

相關文章
相關標籤/搜索