GVKun编程网logo

ReactNative: 了解相机第三方库 react-native-camera 的使用(react native cameraroll)

15

对于ReactNative:了解相机第三方库react-native-camera的使用感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解reactnativecameraroll,并且为您提

对于ReactNative: 了解相机第三方库 react-native-camera 的使用感兴趣的读者,本文将提供您所需要的所有信息,我们将详细讲解react native cameraroll,并且为您提供关于RactNative---react-native-storage使用、React Native -19.React Native Timer定时器的使用、React Native App第三方库、React Native 基于react-native-camera实现扫码功能的宝贵知识。

本文目录一览:

ReactNative: 了解相机第三方库 react-native-camera 的使用(react native cameraroll)

ReactNative: 了解相机第三方库 react-native-camera 的使用(react native cameraroll)

一、简介

在前一篇文章中,初步介绍了 RN 提供的关于相机功能 CameraRoll 的使用了。很多时候,这种最基础的 API 有时很难满足功能需求,此时,如果不想重复造轮子,我们可以选择一个完善好用的第三库。react-native-camera 就是一个非常不错的关于相机功能的第三方库,使用这个框架基本能满足大多数的需求,现在来简单研究一下。

 

二、安装

1、同样地道理,使用之前得先安装,还是采用 npm 安装吧。如下:

//安装react-native-camera
npm install react-native-camera@latest --save

2、安装完成之后,需要添加工程,进行编译配置。执行完了这些步骤后,就完成了第三方库的添加了。

(1) 打开 xcode,找到 Libraries 文件,添加安装的 react-native-camera 目录下的 RNCamera.xcodeproj 工程

(2) 添加 libRNCamera.a 静态库

(3) 找到并选中添加的 RNCamera.xcodeproj 工程,配置 Build Settings 选项卡中的 Header Search Paths 路径

 

三、API

1、类 RNCamera 的定义如下:包含静态常量和函数

//类RNCamera
//继承RNCameraProps属性和ViewProperties属性
export class RNCamera extends Component<RNCameraProps & ViewProperties> {
  
  //静态常量(属性),只读,不可修改
  static Constants: Constants;

  _cameraRef: null | NativeMethodsMixinStatic;
  _cameraHandle: ReturnType<typeof findNodeHandle>;

  //异步拍照  
  //参数 TakePictureOptions:表示的一下拍照时相机的配置选项
  //返回值 Promise: 异步函数,TakePictureResponse为拍照的响应
  takePictureAsync(options?: TakePictureOptions): Promise<TakePictureResponse>;

  //异步录制 
  //参数 RecordOptions:表示的一下录制时相机的配置选项
  //返回值 Promise: 异步函数,RecordResponse为录制的响应
  recordAsync(options?: RecordOptions): Promise<RecordResponse>;

  //刷新认证状态
  //返回值 Promise: 异步函数,void无结果
  refreshAuthorizationStatus(): Promise<void>;

  //停止录制
  stopRecording(): void;

  //暂停预览
  pausePreview(): void;

  //恢复预览
  resumePreview(): void;

  //获取可用的图片的尺寸
  //返回值 Promise: 异步函数,字符串结果
  getAvailablePictureSizes(): Promise<string[]>;

  /** Android only */
  //是否支持异步比率
  getSupportedRatiosAsync(): Promise<string[]>;

  /** iOS only */
  //是否正在录制
  isRecording(): Promise<boolean>;
}

2、关于 RNCameraProps、Constants、TakePictureOptions、TakePictureResponse、RecordOptions、RecordResponse 这些对象的定义,分别如下:

RNCameraProps:相机的配置属性

export interface RNCameraProps {
  children?: ReactNode | FaCC;

  autoFocus?: keyof AutoFocus;
  autoFocusPointOfInterest?: Point;
  /* iOS only */
  onSubjectAreaChanged?: (event: { nativeEvent: { prevPoint: { x: number; y: number; } } }) => void;
  type?: keyof CameraType;
  flashMode?: keyof FlashMode;
  notAuthorizedView?: JSX.Element;
  pendingAuthorizationView?: JSX.Element;
  useCamera2Api?: boolean;
  exposure?: number;
  whiteBalance?: keyof WhiteBalance;
  captureAudio?: boolean;

  onCameraReady?(): void;
  onStatusChange?(event: {
    cameraStatus: keyof CameraStatus;
    recordAudioPermissionStatus: keyof RecordAudioPermissionStatus;
  }): void;
  onMountError?(error: { message: string }): void;

  /** iOS only */
  onAudioInterrupted?(): void;
  onAudioConnected?(): void;

  /** Value: float from 0 to 1.0 */
  zoom?: number;
  /** iOS only. float from 0 to any. Locks the max zoom value to the provided value
    A value <= 1 will use the camera''s max zoom, while a value > 1
    will use that value as the max available zoom
  **/
  maxZoom?: number;
  /** Value: float from 0 to 1.0 */
  focusDepth?: number;

  // -- BARCODE PROPS
  barCodeTypes?: Array<keyof BarCodeType>;
  googleVisionBarcodeType?: Constants[''GoogleVisionBarcodeDetection''][''BarcodeType''];
  googleVisionBarcodeMode?: Constants[''GoogleVisionBarcodeDetection''][''BarcodeMode''];
  onBarCodeRead?(event: {
    data: string;
    rawData?: string;
    type: keyof BarCodeType;
    /**
     * @description For Android use `{ width: number, height: number, origin: Array<Point<string>> }`
     * @description For iOS use `{ origin: Point<string>, size: Size<string> }`
     */
    bounds: { width: number, height: number, origin: Array<Point<string>> } | { origin: Point<string>; size: Size<string> };
  }): void;

  onGoogleVisionBarcodesDetected?(event: {
    barcodes: Barcode[];
  }): void;

  // -- FACE DETECTION PROPS

  onFacesDetected?(response: { faces: Face[] }): void;
  onFaceDetectionError?(response: { isOperational: boolean }): void;
  faceDetectionMode?: keyof FaceDetectionMode;
  faceDetectionLandmarks?: keyof FaceDetectionLandmarks;
  faceDetectionClassifications?: keyof FaceDetectionClassifications;
  trackingEnabled?: boolean;

  onTextRecognized?(response: { textBlocks: TrackedTextFeature[] }): void;
  // -- ANDROID ONLY PROPS
  /** Android only */
  ratio?: string;
  /** Android only - Deprecated */
  permissionDialogTitle?: string;
  /** Android only - Deprecated */
  permissionDialogMessage?: string;
  /** Android only */
  playSoundOnCapture?: boolean;

  androidCameraPermissionOptions?: {
    title: string;
    message: string;
    buttonPositive?: string;
    buttonNegative?: string;
    buttonNeutral?: string;
  } | null;

  androidRecordAudioPermissionOptions?: {
    title: string;
    message: string;
    buttonPositive?: string;
    buttonNegative?: string;
    buttonNeutral?: string;
  } | null;

  // -- IOS ONLY PROPS
  defaultVideoQuality?: keyof VideoQuality;
  /* if true, audio session will not be released on component unmount */
  keepAudioSession?: boolean;
}

Constants:相机的静态常量属性,都是只读的

export interface Constants {
  CameraStatus: CameraStatus;
  AutoFocus: AutoFocus;
  FlashMode: FlashMode;
  VideoCodec: VideoCodec;
  Type: CameraType;
  WhiteBalance: WhiteBalance;
  VideoQuality: VideoQuality;
  BarCodeType: BarCodeType;
  FaceDetection: {
    Classifications: FaceDetectionClassifications;
    Landmarks: FaceDetectionLandmarks;
    Mode: FaceDetectionMode;
  };
  GoogleVisionBarcodeDetection: {
    BarcodeType: GoogleVisionBarcodeType;
    BarcodeMode: GoogleVisionBarcodeMode;
  };
  Orientation: {
    auto: ''auto'';
    landscapeLeft: ''landscapeLeft'';
    landscapeRight: ''landscapeRight'';
    portrait: ''portrait'';
    portraitUpsideDown: ''portraitUpsideDown'';
  };
}

TakePictureOptions:拍照时的配置选项

interface TakePictureOptions {
  quality?: number;
  orientation?: keyof Orientation | OrientationNumber;
  base64?: boolean;
  exif?: boolean;
  width?: number;
  mirrorImage?: boolean;
  doNotSave?: boolean;
  pauseAfterCapture?: boolean;
  writeExif?: boolean | { [name: string]: any };

  /** Android only */
  fixOrientation?: boolean;

  /** iOS only */
  forceUpOrientation?: boolean;
}

TakePictureResponse:拍照后的响应结果

export interface TakePictureResponse {
  width: number;
  height: number;
  uri: string;
  base64?: string;
  exif?: { [name: string]: any };
  pictureOrientation: number;
  deviceOrientation: number;
}

RecordOptions:录制时的配置选项

interface RecordOptions {
  quality?: keyof VideoQuality;
  orientation?: keyof Orientation | OrientationNumber;
  maxDuration?: number;
  maxFileSize?: number;
  mute?: boolean;
  mirrorVideo?: boolean;
  path?: string;
  videoBitrate?: number;

  /** iOS only */
  codec?: keyof VideoCodec | VideoCodec[keyof VideoCodec];
}

RecordResponse:录制后的响应结果

export interface RecordResponse {
  /** Path to the video saved on your app''s cache directory. */
  uri: string;
  videoOrientation: number;
  deviceOrientation: number;
  isRecordingInterrupted: boolean;
  /** iOS only */
  codec: VideoCodec[keyof VideoCodec];
}

3、更多的介绍请查看完整文件,如下:

// Type definitions for react-native-camera 1.0
// Definitions by: Felipe Constantino <https://github.com/fconstant>
//                 Trent Jones <https://github.com/FizzBuzz791>
// If you modify this file, put your GitHub info here as well (for easy contacting purposes)

/*
 * Author notes:
 * I''ve tried to find a easy tool to convert from Flow to Typescript definition files (.d.ts).
 * So we woudn''t have to do it manually... Sadly, I haven''t found it.
 *
 * If you are seeing this from the future, please, send us your cutting-edge technology :) (if it exists)
 */
import { Component, ReactNode } from ''react'';
import { NativeMethodsMixinStatic, ViewProperties, findNodeHandle } from ''react-native'';

type Orientation = Readonly<{
  auto: any;
  landscapeLeft: any;
  landscapeRight: any;
  portrait: any;
  portraitUpsideDown: any;
}>;
type OrientationNumber = 1 | 2 | 3 | 4;
type AutoFocus = Readonly<{ on: any; off: any }>;
type FlashMode = Readonly<{ on: any; off: any; torch: any; auto: any }>;
type CameraType = Readonly<{ front: any; back: any }>;
type WhiteBalance = Readonly<{
  sunny: any;
  cloudy: any;
  shadow: any;
  incandescent: any;
  fluorescent: any;
  auto: any;
}>;
type BarCodeType = Readonly<{
  aztec: any;
  code128: any;
  code39: any;
  code39mod43: any;
  code93: any;
  ean13: any;
  ean8: any;
  pdf417: any;
  qr: any;
  upc_e: any;
  interleaved2of5: any;
  itf14: any;
  datamatrix: any;
}>;
type VideoQuality = Readonly<{
  ''2160p'': any;
  ''1080p'': any;
  ''720p'': any;
  ''480p'': any;
  ''4:3'': any;
  /** iOS Only. Android not supported. */
  ''288p'': any;
}>;
type VideoCodec = Readonly<{
  H264: symbol;
  JPEG: symbol;
  HVEC: symbol;
  AppleProRes422: symbol;
  AppleProRes4444: symbol;
}>;

type FaceDetectionClassifications = Readonly<{ all: any; none: any }>;
type FaceDetectionLandmarks = Readonly<{ all: any; none: any }>;
type FaceDetectionMode = Readonly<{ fast: any; accurate: any }>;
type GoogleVisionBarcodeType = Readonly<{
  CODE_128: any;
  CODE_39: any;
  CODABAR: any;
  DATA_MATRIX: any;
  EAN_13: any;
  EAN_8: any;
  ITF: any;
  QR_CODE: any;
  UPC_A: any;
  UPC_E: any;
  PDF417: any;
  AZTEC: any;
  ALL: any;
}>;
type GoogleVisionBarcodeMode = Readonly<{ NORMAL: any; ALTERNATE: any; INVERTED: any }>;

// FaCC (Function as Child Components)
type Self<T> = { [P in keyof T]: P };
type CameraStatus = Readonly<Self<{ READY: any; PENDING_AUTHORIZATION: any; NOT_AUTHORIZED: any }>>;
type RecordAudioPermissionStatus = Readonly<
  Self<{
    AUTHORIZED: ''AUTHORIZED'';
    PENDING_AUTHORIZATION: ''PENDING_AUTHORIZATION'';
    NOT_AUTHORIZED: ''NOT_AUTHORIZED'';
  }>
>;
type FaCC = (
  params: {
    camera: RNCamera;
    status: keyof CameraStatus;
    recordAudioPermissionStatus: keyof RecordAudioPermissionStatus;
  },
) => JSX.Element;

export interface Constants {
  CameraStatus: CameraStatus;
  AutoFocus: AutoFocus;
  FlashMode: FlashMode;
  VideoCodec: VideoCodec;
  Type: CameraType;
  WhiteBalance: WhiteBalance;
  VideoQuality: VideoQuality;
  BarCodeType: BarCodeType;
  FaceDetection: {
    Classifications: FaceDetectionClassifications;
    Landmarks: FaceDetectionLandmarks;
    Mode: FaceDetectionMode;
  };
  GoogleVisionBarcodeDetection: {
    BarcodeType: GoogleVisionBarcodeType;
    BarcodeMode: GoogleVisionBarcodeMode;
  };
  Orientation: {
    auto: ''auto'';
    landscapeLeft: ''landscapeLeft'';
    landscapeRight: ''landscapeRight'';
    portrait: ''portrait'';
    portraitUpsideDown: ''portraitUpsideDown'';
  };
}

export interface RNCameraProps {
  children?: ReactNode | FaCC;

  autoFocus?: keyof AutoFocus;
  autoFocusPointOfInterest?: Point;
  /* iOS only */
  onSubjectAreaChanged?: (event: { nativeEvent: { prevPoint: { x: number; y: number; } } }) => void;
  type?: keyof CameraType;
  flashMode?: keyof FlashMode;
  notAuthorizedView?: JSX.Element;
  pendingAuthorizationView?: JSX.Element;
  useCamera2Api?: boolean;
  exposure?: number;
  whiteBalance?: keyof WhiteBalance;
  captureAudio?: boolean;

  onCameraReady?(): void;
  onStatusChange?(event: {
    cameraStatus: keyof CameraStatus;
    recordAudioPermissionStatus: keyof RecordAudioPermissionStatus;
  }): void;
  onMountError?(error: { message: string }): void;

  /** iOS only */
  onAudioInterrupted?(): void;
  onAudioConnected?(): void;

  /** Value: float from 0 to 1.0 */
  zoom?: number;
  /** iOS only. float from 0 to any. Locks the max zoom value to the provided value
    A value <= 1 will use the camera''s max zoom, while a value > 1
    will use that value as the max available zoom
  **/
  maxZoom?: number;
  /** Value: float from 0 to 1.0 */
  focusDepth?: number;

  // -- BARCODE PROPS
  barCodeTypes?: Array<keyof BarCodeType>;
  googleVisionBarcodeType?: Constants[''GoogleVisionBarcodeDetection''][''BarcodeType''];
  googleVisionBarcodeMode?: Constants[''GoogleVisionBarcodeDetection''][''BarcodeMode''];
  onBarCodeRead?(event: {
    data: string;
    rawData?: string;
    type: keyof BarCodeType;
    /**
     * @description For Android use `{ width: number, height: number, origin: Array<Point<string>> }`
     * @description For iOS use `{ origin: Point<string>, size: Size<string> }`
     */
    bounds: { width: number, height: number, origin: Array<Point<string>> } | { origin: Point<string>; size: Size<string> };
  }): void;

  onGoogleVisionBarcodesDetected?(event: {
    barcodes: Barcode[];
  }): void;

  // -- FACE DETECTION PROPS

  onFacesDetected?(response: { faces: Face[] }): void;
  onFaceDetectionError?(response: { isOperational: boolean }): void;
  faceDetectionMode?: keyof FaceDetectionMode;
  faceDetectionLandmarks?: keyof FaceDetectionLandmarks;
  faceDetectionClassifications?: keyof FaceDetectionClassifications;
  trackingEnabled?: boolean;

  onTextRecognized?(response: { textBlocks: TrackedTextFeature[] }): void;
  // -- ANDROID ONLY PROPS
  /** Android only */
  ratio?: string;
  /** Android only - Deprecated */
  permissionDialogTitle?: string;
  /** Android only - Deprecated */
  permissionDialogMessage?: string;
  /** Android only */
  playSoundOnCapture?: boolean;

  androidCameraPermissionOptions?: {
    title: string;
    message: string;
    buttonPositive?: string;
    buttonNegative?: string;
    buttonNeutral?: string;
  } | null;

  androidRecordAudioPermissionOptions?: {
    title: string;
    message: string;
    buttonPositive?: string;
    buttonNegative?: string;
    buttonNeutral?: string;
  } | null;

  // -- IOS ONLY PROPS
  defaultVideoQuality?: keyof VideoQuality;
  /* if true, audio session will not be released on component unmount */
  keepAudioSession?: boolean;
}

interface Point<T = number> {
  x: T;
  y: T;
}

interface Size<T = number> {
  width: T;
  height: T;
}

export interface Barcode {
  bounds: {
    size: Size;
    origin: Point;
  };
  data: string;
  dataRaw: string;
  type: BarcodeType;
  format?: string;
  addresses?: {
    addressesType?: "UNKNOWN" | "Work" | "Home";
    addressLines?: string[];
  }[];
  emails?: Email[];
  phones?: Phone[];
  urls?: string[];
  name?: {
    firstName?: string;
    lastName?: string;
    middleName?: string;
    prefix?:string;
    pronounciation?:string;
    suffix?:string;
    formattedName?: string;
  };
  phone?: Phone;
  organization?: string;
  latitude?: number;
  longitude?: number;
  ssid?: string;
  password?: string;
  encryptionType?: string;
  title?: string;
  url?: string;
  firstName?: string;
  middleName?: string;
  lastName?: string;
  gender?: string;
  addressCity?: string;
  addressState?: string;
  addressStreet?: string;
  addressZip?: string;
  birthDate?: string;
  documentType?: string;
  licenseNumber?: string;
  expiryDate?: string;
  issuingDate?: string;
  issuingCountry?: string;
  eventDescription?: string;
  location?: string;
  organizer?: string;
  status?: string;
  summary?: string;
  start?: string;
  end?: string;
  email?: Email;
  phoneNumber?: string;
  message?: string;
}

export type BarcodeType =
  |"EMAIL"
  |"PHONE"
  |"CALENDAR_EVENT"
  |"DRIVER_LICENSE"
  |"GEO"
  |"SMS"
  |"CONTACT_INFO"
  |"WIFI"
  |"TEXT"
  |"ISBN"
  |"PRODUCT"
  |"URL"

export interface Email {
  address?: string;
  body?: string;
  subject?: string;
  emailType?: "UNKNOWN" | "Work" | "Home";
}

export interface Phone {
  number?: string;
  phoneType?: "UNKNOWN" | "Work" | "Home" | "Fax" | "Mobile";
}

export interface Face {
  faceID?: number;
  bounds: {
    size: Size;
    origin: Point;
  };
  smilingProbability?: number;
  leftEarPosition?: Point;
  rightEarPosition?: Point;
  leftEyePosition?: Point;
  leftEyeOpenProbability?: number;
  rightEyePosition?: Point;
  rightEyeOpenProbability?: number;
  leftCheekPosition?: Point;
  rightCheekPosition?: Point;
  leftMouthPosition?: Point;
  mouthPosition?: Point;
  rightMouthPosition?: Point;
  bottomMouthPosition?: Point;
  noseBasePosition?: Point;
  yawAngle?: number;
  rollAngle?: number;
}

export interface TrackedTextFeature {
  type: ''block'' | ''line'' | ''element'';
  bounds: {
    size: Size;
    origin: Point;
  };
  value: string;
  components: TrackedTextFeature[];
}

interface TakePictureOptions {
  quality?: number;
  orientation?: keyof Orientation | OrientationNumber;
  base64?: boolean;
  exif?: boolean;
  width?: number;
  mirrorImage?: boolean;
  doNotSave?: boolean;
  pauseAfterCapture?: boolean;
  writeExif?: boolean | { [name: string]: any };

  /** Android only */
  fixOrientation?: boolean;

  /** iOS only */
  forceUpOrientation?: boolean;
}

export interface TakePictureResponse {
  width: number;
  height: number;
  uri: string;
  base64?: string;
  exif?: { [name: string]: any };
  pictureOrientation: number;
  deviceOrientation: number;
}

interface RecordOptions {
  quality?: keyof VideoQuality;
  orientation?: keyof Orientation | OrientationNumber;
  maxDuration?: number;
  maxFileSize?: number;
  mute?: boolean;
  mirrorVideo?: boolean;
  path?: string;
  videoBitrate?: number;

  /** iOS only */
  codec?: keyof VideoCodec | VideoCodec[keyof VideoCodec];
}

export interface RecordResponse {
  /** Path to the video saved on your app''s cache directory. */
  uri: string;
  videoOrientation: number;
  deviceOrientation: number;
  isRecordingInterrupted: boolean;
  /** iOS only */
  codec: VideoCodec[keyof VideoCodec];
}

export class RNCamera extends Component<RNCameraProps & ViewProperties> {
  static Constants: Constants;

  _cameraRef: null | NativeMethodsMixinStatic;
  _cameraHandle: ReturnType<typeof findNodeHandle>;

  takePictureAsync(options?: TakePictureOptions): Promise<TakePictureResponse>;
  recordAsync(options?: RecordOptions): Promise<RecordResponse>;
  refreshAuthorizationStatus(): Promise<void>;
  stopRecording(): void;
  pausePreview(): void;
  resumePreview(): void;
  getAvailablePictureSizes(): Promise<string[]>;

  /** Android only */
  getSupportedRatiosAsync(): Promise<string[]>;

  /** iOS only */
  isRecording(): Promise<boolean>;
}

interface DetectionOptions {
  mode?: keyof FaceDetectionMode;
  detectLandmarks?: keyof FaceDetectionLandmarks;
  runClassifications?: keyof FaceDetectionClassifications;
}

export class FaceDetector {
  private constructor();
  static Constants: Constants[''FaceDetection''];
  static detectFacesAsync(uri: string, options?: DetectionOptions): Promise<Face[]>;
}

// -- DEPRECATED CONTENT BELOW

/**
 * @deprecated As of 1.0.0 release, RCTCamera is deprecated. Please use RNCamera for the latest fixes and improvements.
 */
export default class RCTCamera extends Component<any> {
  static constants: any;
}
View Code

 

四、使用

实现一个摄像头切换、扫描二维码功能。(模拟器无法调起相机)请使用真机测试。

切记要添加授权字段:Privacy - Camera Usage Description、Privacy - Microphone Usage Description。代码示例如下:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from ''react'';

import {
    AppRegistry,
    StyleSheet,
    Text,
    TouchableHighlight
} from ''react-native'';

import {RNCamera, TakePictureResponse} from ''react-native-camera'';

export default class ReactNativeDemo extends Component {

    //设置当前摄像头为后置摄像头
    state = { cameraType: RNCamera.Constants.Type.back };

    //扫描二维码
    _onBarCodeRead(e){
        //data: string;
        //rawData?: string;
        //type: keyof BarCodeType;
        //bounds:
          //For iOS use `{ origin: Point<string>, size: Size<string> }`
          //For Android use `{ width: number, height: number, origin: Array<Point<string>> }`
        console.log(e)
    }

    //切换摄像头方向     undefined is not an object (evaluating ''state.cameraType'')
    _switchCamera(){
        this.setState({
            cameraType: (this.state.cameraType === RNCamera.Constants.Type.back) ?
                RNCamera.Constants.Type.front : RNCamera.Constants.Type.back
        })
        // let state = this.state;
        // state.cameraType = (state.cameraType === RNCamera.Constants.Type.back) ?
        //     RNCamera.Constants.Type.front : RNCamera.Constants.Type.back;
        // this.setState(state);
    }

    //拍摄照片
    _takePicture(){
        this.refs.camera.takePictureAsync().then( (response) => {
            console.log("response.uri:"+response.uri)
        }).catch((error => {
            console.log("error:"+error)
        }))
    }

    render() {
        return (
            <RNCamera
                ref="camera"
                style={styles.container}
                onBarCodeRead={this._onBarCodeRead.bind(this)}
                type={this.state.cameraType}
            >
                <TouchableHighlight onPress={this._switchCamera.bind(this)}>
                    <Text style={styles.switch}>Switch Camera</Text>
                </TouchableHighlight>

                <TouchableHighlight onPress={this._takePicture.bind(this)}>
                    <Text style={styles.picture}>Take Picture</Text>
                </TouchableHighlight>
            </RNCamera>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: ''center'',
        alignItems: ''center'',
        backgroundColor: ''transparent''
    },
    switch: {
        marginTop: 30,
        textAlign: ''center'',
        fontSize: 30,
        color: ''red''
    },
    picture: {
        marginTop: 30,
        textAlign: ''center'',
        fontSize: 30,
        color: ''red''
    }
});

AppRegistry.registerComponent(''ReactNativeDemo'', () => ReactNativeDemo);

模拟器运行出现,无法启动相机如下

                                                           

真机运行出现,对相机和麦克风进行授权后,示例如下

 

拍照时的打印日至如下:

2020-01-11 10:52:23.940 [info][tid:com.facebook.react.JavaScript] response.uri:file:///var/mobile/Containers/Data/Application/F36A8D1A-2F96-4E24-B2D8-9755AD1FF488/Library/Caches/Camera/F4B30B4E-69C5-4D61-B2AE-99426AED52F2.jpg

RactNative---react-native-storage使用

RactNative---react-native-storage使用

这是一个本地持久存储的封装,可以同时支持react-native(AsyncStorage)和浏览器(localStorage)。ES6语法,promise异步读取,使用jest进行了完整的单元测试。

安装

npm install react-native-storage --save

React Native开发

无需配置直接使用(但要求0.13或更高版本)。

导入

import Storage from 'react-native-storage';
请勿使用require(‘react-native-storage’)语法,否则在react native 0.16之后的版本中会报错.

初始化

import { AsyncStorage } from 'react-native';
var storage = new Storage({
 // 最大容量,默认值1000条数据循环存储
size: 1000,// 存储引擎:对于RN使用AsyncStorage,对于web使
 用window.localStorage
  // 如果不指定则数据只会保存在内存中,重启后即丢失
  storageBackend: AsyncStorage,// 数据过期时间,默认一整天(1000 * 3600 * 24 毫秒),设为null则永不过期
  defaultExpires: 1000 * 3600 * 24,// 读写时在内存中缓存数据。默认启用。
  enableCache: true,// 如果storage中没有相应数据,或数据已过期,
  // 则会调用相应的sync方法,无缝返回最新数据。
  // sync方法的具体说明会在后文提到
  // 你可以在构造函数这里就写好sync的方法
  // 或是写到另一个文件里,这里require引入
  // 或是在任何时候,直接对storage.sync进行赋值修改
  sync: require('./sync')  // 这个sync文件是要你自己写的
})  
// 最好在全局范围内创建一个(且只有一个)storage实例,方便直接调用
// 对于web
// window.storage = storage;
// 对于react native
// global.storage = storage;
// 这样,在此**之后**的任意位置即可以直接调用storage
// 注意:全局变量一定是先声明,后使用
// 如果你在某处调用storage报错未定义
// 请检查global.storage = storage语句是否确实已经执行过了
不了解全局变量的使用?请点这里 https://github.com/sunnylqm/react-native-storage/issues/29

保存、读取和删除

// 使用key来保存数据。这些数据一般是全局独有的,常常需要调用的。
  // 除非你手动移除,这些数据会被永久保存,而且默认不会过期。
  storage.save({
    key: 'loginState',// 注意:请不要在key中使用_下划线符号!
    data: { 
      from: 'some other site',userid: 'some userid',token: 'some token'
    },// 如果不指定过期时间,则会使用defaultExpires参数
    // 如果设为null,则永不过期
    expires: 1000 * 3600
  });

  // 读取
  storage.load({
    key: 'loginState',// autoSync(默认为true)意味着在没有找到数据或数据过期时自动调用相应的sync方法
    autoSync: true,// syncInBackground(默认为true)意味着如果数据过期,
    // 在调用sync方法的同时先返回已经过期的数据。
    // 设置为false的话,则始终强制返回sync方法提供的最新数据(当然会需要更多等待时间)。
    syncInBackground: true,// 你还可以给sync方法传递额外的参数
    syncParams: {
      extraFetchOptions: {
        // 各种参数
      },someFlag: true,},}).then(ret => {
    // 如果找到数据,则在then方法中返回
    // 注意:这是异步返回的结果(不了解异步请自行搜索学习)
    // 你只能在then这个方法内继续处理ret数据
    // 而不能在then以外处理
    // 也没有办法“变成”同步返回
    // 你也可以使用“看似”同步的async/await语法

    console.log(ret.userid);
    this.setState({ user: ret });
  }).catch(err => {
    //如果没有找到数据且没有sync方法,
    //或者有其他异常,则在catch中返回
    console.warn(err.message);
    switch (err.name) {
        case 'NotFoundError':
            // Todo;
            break;
        case 'ExpiredError':
            // Todo
            break;
    }
  })
  // 使用key和id来保存数据,一般是保存同类别(key)的大量数据。
  // 所有这些"key-id"数据共有一个保存上限(无论是否相同key)
  // 即在初始化storage时传入的size参数。
  // 在默认上限参数下,第1001个数据会覆盖第1个数据。
  // 覆盖之后,再读取第1个数据,会返回catch或是相应的sync方法。
  var userA = {
    name: 'A',age: 20,tags: [
      'geek','nerd','otaku'
    ]
  };

  storage.save({
    key: 'user',// 注意:请不要在key中使用_下划线符号!
    id: '1001',// 注意:请不要在id中使用_下划线符号!
    data: userA,expires: 1000 * 60   
  });

  //load 读取
  storage.load({
    key: 'user',id: '1001'
  }).then(ret => {
    // 如果找到数据,则在then方法中返回
    console.log(ret.userid);
  }).catch(err => {
    // 如果没有找到数据且没有sync方法,
    // 或者有其他异常,则在catch中返回
    console.warn(err.message);
    switch (err.name) {
        case 'NotFoundError':
            // Todo;
            break;
        case 'ExpiredError':
            // Todo
            break;
    }
  })

// --------------------------------------------------

// 获取某个key下的所有id
storage.getIdsForKey('user').then(ids => {
    console.log(ids);
});

// 获取某个key下的所有数据
storage.getAllDataForKey('user').then(users => {
    console.log(users);
});

// !! 清除某个key下的所有数据
storage.clearMapForKey('user');

// -------------------------------------------------- 

// 删除单个数据
storage.remove({
    key: 'lastPage'
});
storage.remove({
    key: 'user',id: '1001'
});

// !! 清空map,移除所有"key-id"数据(但会保留只有key的数据)
storage.clearMap();

同步远程数据(刷新)

storage.sync = {
    // sync方法的名字必须和所存数据的key完全相同
    // 方法接受的参数为一整个object,所有参数从object中解构取出
    // 这里可以使用promise。或是使用普通回调函数,但需要调用resolve或reject。
    user(params){
      let { id,resolve,reject,syncParams: { extraFetchOptions,someFlag } } = params;
      fetch('user/',{
        method: 'GET',body: 'id=' + id,...extraFetchOptions,}).then(response => {
        return response.json();
      }).then(json => {
        //console.log(json);
        if(json && json.user){
          storage.save({
            key: 'user',id,data: json.user
          });

          if (someFlag) {
            // 根据syncParams中的额外参数做对应处理
          }

          // 成功则调用resolve
          resolve && resolve(json.user);
        }
        else{
          // 失败则调用reject
          reject && reject(new Error('data parse error'));
        }
      }).catch(err => {
        console.warn(err);
        reject && reject(err);
      });
    }
  }

有了上面这个sync方法,以后再调用storage.load时,如果本地并没有存储相应的user,那么会自动触发storage.sync.user去远程取回数据并无缝返回。

  storage.load({
    key: 'user',id: '1002'
  }).then(...)
 ``` 读取批量数据 ```// 使用和load方法一样的参数读取批量数据,但是参数是以数组的方式提供。
// 会在需要时分别调用相应的sync方法,最后统一返回一个有序数组。
storage.getBatchData([
    { key: 'loginState' },{ key: 'checkPoint',syncInBackground: false },{ key: 'balance' },{ key: 'user',id: '1009' }
])
.then(results => {
  results.forEach( result => {
    console.log(result);    
  })
})

//根据key和一个id数组来读取批量数据
storage.getBatchDataWithIds({
  key: 'user',ids: ['1001','1002','1003']
})
.then( ... ).

这两个方法除了参数形式不同,还有个值得注意的差异。getBatchData会在数据缺失时挨个调用不同的sync方法(因为key不同)。但是getBatchDataWithIds却会把缺失的数据统计起来,将它们的id收集到一个数组中,然后一次传递给对应的sync方法(避免挨个查询导致同时发起大量请求),所以你需要在服务端实现通过数组来查询返回,还要注意对应的sync方法的参数处理(因为id参数可能是一个字符串,也可能是一个数组的字符串)。

React Native -19.React Native Timer定时器的使用

React Native -19.React Native Timer定时器的使用

React Native -19.React Native Timer定时器的使用

背景:React Native Version:0.3.1
语法 ES6

Step1:介绍

RN的定时器就是一个创建方法。并没有像iOS一样的NSTimer类
根据官方提供的文档,定时器有四种形式: • setTimeout,clearTimeout
• setInterval,clearInterval
• setImmediate,clearImmediate
• requestAnimationFrame,cancelAnimationFrame

  • 见名思义:set和request方法是创建。clear是清除,清除必须有.

Step1:使用

  • setTimeout(function,time)

    function:触发的方法
    time:延迟时间 毫秒
    效果:延迟time时间后执行function,

  • setInterval(function,time)

    function:触发的方法
    time:延迟时间 毫秒
    效果:间隔time时间后执行function

  • setImmediate(function,time)

    function:触发的方法
    time:延迟时间 毫秒
    效果:间隔time时间后(立即)执行function,

  • setImmediate此方法会在js代码块之行结束后执行,就在就要发送批量相应数据到原生之前,如过再方法中又掉用了此方法,他会立即呗掉用。而不会再掉用之前等待原生代码

    以上这句话从官网文档翻译过来,等待以后实际验证。

Step2:实战

import React,{Component} from 'react';
import {AppRegistry,StyleSheet,ActivityIndicator} from 'react-native';

class hello extends Component {
  constructor(props:any){
    super(props);
    var timer1=null;
    var timer2=null;
    var timer3=null;
    this.state = {
      animating: true,};
  }

  componentDidMount(){
    this.timer1 = setInterval(
      ()=>{
        this._consolelogshow();
      },2000,);

    this.timer2 = setTimeout(
      ()=>{console.log('setTimeout22222222'); },1000,);

    this.timer3 = setImmediate(
      ()=>{console.log('setImmediate333333');},3000,);
  }

  componentwillUnmount() {
    // 如果存在this.timer,则使用clearTimeout清空。
    // 如果你使用多个timer,那么用多个变量,或者用个数组来保存引用,然后逐个clear
    this.timer1 && clearInterval(this.timer1);
    this.timer2 && clearTimeout(this.timer2);
    this.timer3 && clearImmediate(this.timer3);

  }

  _consolelogshow(){
    console.log('把一个定时器的引用挂在this上11111');
  }

  render(){
    return(
      <ActivityIndicator
        animating={this.state.animating}
        style={[styles.centerting,{height:80}]}
        size="large"/>
    )
  }
}


 var styles = StyleSheet.create({
   centering: {
    alignItems: 'center',justifyContent: 'center',padding: 8,}
 });

AppRegistry.registerComponent('hello',()=>hello); 

Step3:实战解读

  • componentDidMount 在生命周期组件加载成功后的方法里创建三个定时器。
  • constructor 方法中声明三个定时器变量,方便全局掉用
  • componentwillUnmount 在生命周期组件将要移除的方法里清除定时器。避免crash

React Native App第三方库

React Native App第三方库

lottie-react-native动画库github以及官网

可以用于页面加载时的动画

import React, {FC} from "react";
import {SafeAreaView, ScrollView, Text} from "react-native";
import Lottie from ''lottie-react-native'';


interface Props {

}

const LottieReactNative: FC<Props> = () => {
    const animationRef = React.useRef<Lottie | null>(null);

    React.useEffect(() => {
       
    }, []);

    return <SafeAreaView>
        <ScrollView>
            <Lottie source={require(''./animation.json'')} autoPlay loop style={{width: 100}}/>
        </ScrollView>
    </SafeAreaView>;
};

export default LottieReactNative;

192B37FF-0A1A-4A48-8A79-87D44B0FD655-85254-000044162DF453EB.GIF

react-icomoon轻量图标

import React from "react";
import {SafeAreaView, ScrollView} from "react-native";
import IcoMoon, {iconList} from "react-icomoon";
const iconSet = require(''./selection.json'');
import { Svg, Path } from ''react-native-svg'';

const ReactIcoMoon = () => {
    React.useEffect(() => {

    }, []);
    return <SafeAreaView>
        <ScrollView>
            <IcoMoon
                native
                iconSet={iconSet}
                SvgComponent={Svg}
                PathComponent={Path}
                icon="heart"
                size={30}
                style={{ margin: 50, color: ''#f40'' }}
            />
        </ScrollView>
    </SafeAreaView>;
};

export default ReactIcoMoon;

WechatIMG85.jpeg

react-native-calendars日历

import React from "react";
import {SafeAreaView, ScrollView} from "react-native";
import {Calendar, LocaleConfig} from ''react-native-calendars'';

LocaleConfig[''locales''][''''] = {
    monthNames: [''一月'', ''二月'', ''三月'', ''四月'', ''五月'', ''六月'', ''七月'', ''八月'', ''九月'', ''十月'', ''十一月'', ''十二月''],
    monthNamesShort: [''一月'', ''二月'', ''三月'', ''四月'', ''五月'', ''六月'', ''七月'', ''八月'', ''九月'', ''十月'', ''十一月'', ''十二月''],
    dayNames: [''周一'', ''周二'', ''周三'', ''周四'', ''周五'', ''周六'', ''周天''],
    dayNamesShort: [''周一'', ''周二'', ''周三'', ''周四'', ''周五'', ''周六'', ''周天''],
    amDesignator: ''上午'',
    pmDesignator: ''下午''
}
const ReactNativeCalendars = () => {
    const [selected, setSelected] = React.useState('''');
    React.useEffect(() => {

    }, []);
    return <SafeAreaView>
        <ScrollView>
            <Calendar
                onDayPress={(day) => {
                    setSelected(day.dateString);
                }}
                style={{height: 350}}
                markedDates={{
                    [selected]: {selected: true, disableTouchEvent: true, selectedDotColor: ''orange''}
                }}
            />
        </ScrollView>
    </SafeAreaView>;
};

export default ReactNativeCalendars;

E9674479-3137-411D-B674-EEDEBBE27A61-85254-0000443FCD644D9E.GIF

react-native-drawer抽屉

<Drawer
     type="displace"
     ref={drawerRef}
     acceptDoubleTap={true}
     captureGestures={false}
     tweenDuration={100}
     panThreshold={0.08}
     onOpen={() => setOpen(true)}
     onClose={() => setOpen(false)}
     openDrawerOffset={() => 90}
     panOpenMask={0.2}
     open={open}
     negotiatePan={true}
     content={<ScrollView style={styles.container}>
         <ReactNativeCalendars onSelect={(date) => {
             drawerRef.current!.close();
             setDate(date);
         }}/>
     </ScrollView>}
     styles={{drawer: styles.drawer, main: styles.main}}
     tweenHandler={(ratio) => ({
         main: {opacity: (2 - ratio) / 2}
     })}
 >
     <ScrollView style={[styles.container, {backgroundColor: ''#FFF'',}]}>
         <Text onPress={() => drawerRef.current!.open()}>{date || ''选择日期''}</Text>
     </ScrollView>
</Drawer>

D8A133C5-4CB8-43C5-BEB2-435F39B112A0-94224-00004579DD804CF7.GIF

react-native-textinput-effects漂亮的输入框

import React from "react";
import {SafeAreaView, ScrollView} from "react-native";
import {Fumi} from ''react-native-textinput-effects'';
import FontAwesomeIcon from ''react-native-vector-icons/FontAwesome'';


const ReactNativeTextInputEffects = () => {
    const [value, setValue] = React.useState('''');
    React.useEffect(() => {

    }, []);
    return <SafeAreaView>
        <ScrollView>
            <Fumi
                style={{backgroundColor: ''#f9f5ed''}}
                label="账号"
                iconClass={FontAwesomeIcon}
                iconName="university"
                iconColor="#f95a25"
                iconSize={20}
                iconWidth={40}
                inputPadding={16}
                onChangeText={setValue}
                value={value}
                keyboardType="number-pad"
                blurOnSubmit={true}
            />
        </ScrollView>
    </SafeAreaView>;
};

export default ReactNativeTextInputEffects;

IMG_9131.GIF

react-native-lightbox查看图片(这个库有点老复制下来重写)

import React, {FC} from "react";
import {
    View,
    ViewStyle,
    Animated,
    Easing,
    Dimensions,
    Platform,
    StyleSheet,
    StatusBar,
    Modal,
    TouchableOpacity,
    Text, TouchableWithoutFeedback, Image
} from "react-native";


const WINDOW_HEIGHT = Dimensions.get(''window'').height;
const WINDOW_WIDTH = Dimensions.get(''window'').width;
const isIOS = Platform.OS === ''ios'';

export interface ImageLightBoxProps {
    style?: ViewStyle;
    uri: string;
}

const styles = StyleSheet.create({
    background: {
        position: ''absolute'',
        top: 0,
        left: 0,
        width: WINDOW_WIDTH,
        height: WINDOW_HEIGHT,
        backgroundColor: ''#000''
    },
    open: {
        position: ''absolute'',
        flex: 1,
        justifyContent: ''center'',
        backgroundColor: ''transparent'',
    },
    header: {
        position: ''absolute'',
        top: 0,
        left: 0,
        width: WINDOW_WIDTH,
        backgroundColor: ''transparent'',
    },
    closeButton: {
        fontSize: 35,
        color: ''white'',
        lineHeight: 40,
        width: 40,
        textAlign: ''center'',
        shadowOffset: {
            width: 0,
            height: 0,
        },
        shadowRadius: 1.5,
        shadowColor: ''black'',
        shadowOpacity: 0.8,
    },
});

const springConfig = {
    tension: 1,
    friction: 100,
    useNativeDriver: false
}
const ImageLightBox: FC<ImageLightBoxProps> = (props) => {
    const rootRef = React.useRef<Image>();
    const animatedLayoutOpacity = React.useRef(new Animated.Value(1));
    const animatedOpenValue = React.useRef(new Animated.Value(0));
    const [origin, setOrigin] = React.useState({x: 0, y: 0, width: 0, height: 0});
    const [isOpen, setIsOpen] = React.useState(false);
    const [target, setTarget] = React.useState({x: 0, y: 0, opacity: 1});
    const [imageStyle, setImageStyle] = React.useState<{ height?: number | string; width?: number | string }>({});

    React.useEffect(() => {
        Image.getSize(props.uri, (w, h) => {
            if (props.style) {
                const {height, width} = props.style;
                if (height && !width) {
                    setImageStyle({width: height * w / h, height});
                } else if (!height && width) {
                    setImageStyle({width, height: width * h / w});
                } else if (height && width) {
                    setImageStyle({width, height});
                } else {
                    setImageStyle({height: h, width: w})
                }
            }
        });
    }, [props.uri]);

    const open = () => {
        rootRef.current!.measure((ox, oy, width, height, px, py) => {
            if (isIOS) {
                StatusBar.setHidden(true, ''fade'');
            }
            setIsOpen(true);
            setTarget({x: 0, y: 0, opacity: 1});
            // 获取图片的位置并隐身
            setOrigin({width, height, x: px, y: py});
            Animated.parallel([
                setOpacity(0),
                setOpenValue(1)
            ]).start();
        });
    }

    const close = () => {
        if (isIOS) {
            StatusBar.setHidden(false, ''fade'');
        }
        setOpenValue(0).start(() => {
            setOpacity(1).start(() => {
                setIsOpen(false);
            });
        });
    }

    const setOpenValue = (value) => {
        return Animated.spring(animatedOpenValue.current, {
            toValue: value,
            ...springConfig
        });
    }
    const setOpacity = (value: number) => {
        return Animated.timing(animatedLayoutOpacity.current, {
            useNativeDriver: true,
            easing: Easing.linear,
            duration: 100,
            toValue: value
        });
    }

    const opacityStyle = {
        opacity: animatedOpenValue.current.interpolate({inputRange: [0, 1], outputRange: [0, target.opacity]})
    }
    const openStyle = [styles.open, {
        left: animatedOpenValue.current.interpolate({
            inputRange: [0, 1],
            outputRange: [origin.x || 0, target.x || 0],
        }),
        top: animatedOpenValue.current.interpolate({
            inputRange: [0, 1],
            outputRange: [origin.y || 0, target.y || 0],
        }),
        width: animatedOpenValue.current.interpolate({
            inputRange: [0, 1],
            outputRange: [origin.width || 0, WINDOW_WIDTH],
        }),
        height: animatedOpenValue.current.interpolate({
            inputRange: [0, 1],
            outputRange: [origin.height || 0, WINDOW_HEIGHT],
        })
    }];

    return <React.Fragment>
        <TouchableWithoutFeedback onPress={open}>
            <View ref={rootRef} style={imageStyle}>
                <Animated.Image
                    style={[props.style, imageStyle, {opacity: animatedLayoutOpacity.current}]}
                    resizeMode="contain"
                    source={{uri: props.uri}}
                />
            </View>
        </TouchableWithoutFeedback>
        <Modal visible={isOpen} transparent={true} onRequestClose={close}>
            <Animated.View style={[styles.background, opacityStyle]}/>
            <Animated.View style={[openStyle]}>
                <Image
                    source={{uri: props.uri}}
                    resizeMode="contain"
                    style={{flex: 1}}
                />
            </Animated.View>
            <Animated.View style={[styles.header, opacityStyle]}>
                <TouchableOpacity onPress={close}>
                    <Text style={styles.closeButton}>×</Text>
                </TouchableOpacity>
            </Animated.View>
        </Modal>
    </React.Fragment>;
};

export default ImageLightBox;
import React from "react";
import {SafeAreaView, ScrollView, View} from "react-native";
import ImageLightBox from "../../components/ImageLightBox";


const ReactNativeLightBox = () => {
    React.useEffect(() => {

    }, []);
    return <SafeAreaView>
        <ScrollView style={{paddingTop: 100}}>
            <View style={{flexDirection: "row", justifyContent: "space-between"}}>
                <ImageLightBox
                    uri="https://xxx.jpeg"
                    style={{height: 40}}
                />
                <ImageLightBox
                    uri="https://xxx.jpeg"
                    style={{height: 100}}
                />
                <ImageLightBox
                    uri="https://xxx.jpeg"
                    style={{height: 200}}
                />
            </View>
        </ScrollView>
    </SafeAreaView>;
};

export default ReactNativeLightBox;

IMG_9140.GIF

react-native-action-button浮动折叠菜单

import React from "react";
import {SafeAreaView, ScrollView, StyleSheet} from "react-native";
import ActionButton from ''react-native-action-button'';
import Icon from ''react-native-vector-icons/Ionicons'';


const ReactNativeActionButton = () => {
    React.useEffect(() => {

    }, []);
    return <SafeAreaView style={{flex: 1, backgroundColor: ''#f3f3f3''}}>
        <ScrollView>

        </ScrollView>
        <ActionButton buttonColor="rgba(231,76,60,1)">
            <ActionButton.Item buttonColor=''#9b59b6'' title="新任务" onPress={() => {}}>
                <Icon name="md-create" style={styles.actionButtonIcon} />
            </ActionButton.Item>
            <ActionButton.Item buttonColor=''#3498db'' title="新消息" onPress={() => {}}>
                <Icon name="md-notifications-off" style={styles.actionButtonIcon} />
            </ActionButton.Item>
            <ActionButton.Item buttonColor=''#1abc9c'' title="新问题" onPress={() => {}}>
                <Icon name="md-done-all" style={styles.actionButtonIcon} />
            </ActionButton.Item>
        </ActionButton>
    </SafeAreaView>;
};

const styles = StyleSheet.create({
    actionButtonIcon: {
        fontSize: 20,
        height: 22,
        color: ''white'',
    },
});

export default ReactNativeActionButton;

593255E0-219F-4BF8-9B17-C9BC98C8BFCD-12038-0000492FA3F7E3BB.GIF

react-native-masonry砖块格子类似小红书

这个库的里的ListView不能用了,需要替换成deprecated-react-native-listview,顺便把查看图片也加了进去,建议用最新的FlatList去重写

import React from "react";
import {SafeAreaView} from "react-native";
import Masonry from "../../components/Masonry";

const ReactNativeMasonry = () => {
    React.useEffect(() => {

    }, []);
    return <SafeAreaView style={{flex: 1}}>
        <Masonry
            columns={2}
            bricks={[
                {uri: ''https://xxx''},
                {uri: ''https://xxx''},
                {uri: ''https://xxx''},
                {uri: ''https://xxx''},
                {uri: ''https://xxx''},
                {uri: ''https://xxx''},
                {uri: ''https://xxx''},
                {uri: ''https://xxx''},
                {uri: ''https://xxx''},
            ]}
        />
    </SafeAreaView>;
};

export default ReactNativeMasonry;

7E4E2CEA-7CFA-4C64-9681-C88F564803DE-33154-00004995FD1BC83F.GIF

react-native-looped-carousel反复的轮播(图片)

和react-native-swiper和react-native-carousel差不多。
图片浏览的轮播用react-native-image-carousel可以放大缩小
react-native-fading-slides轮播幻灯片没什么用

import React, {FC} from "react";
import {View, Image} from "react-native";
import Carousel from ''react-native-looped-carousel'';

interface Props {
    images?: { uri: string }[];
    currentPage?: number;
    onAnimateNextPage?: (page: number) => void;
}

const Images: FC<Props> = (props) => {
    const [size, setSize] = React.useState<{ width?: number, height?: number }>({});
    React.useEffect(() => {

    }, []);
    return <View style={{width: ''100%'', height: ''100%''}} onLayout={(e) => {
        const layout = e[''nativeEvent''].layout;
        setSize({width: layout.width, height: layout.height});
    }}>
        <Carousel
            currentPage={props.currentPage}
            pageInfo={true}
            style={{...size}}
            autoplay={false}
            pageInfoBackgroundColor="rgba(255,255,255,0.5)"
            onAnimateNextPage={props.onAnimateNextPage}>
            {props.images!.map((i, index) => {
                return <View style={[size]} key={index}>
                    <Image
                        source={{uri: i.uri}}
                        resizeMode="contain"
                        style={{flex: 1, }}
                    />
                </View>
            })}
        </Carousel>
    </View>;
};

Images.defaultProps = {
    images: [],
    currentPage: 0,
}
export default Images;

7D6936A9-FEBB-4EDB-B032-B5294667EBC5-42276-00004BFA9E68428D.GIF

react-native-svg-charts图表

import React from "react";
import {SafeAreaView, ScrollView, View} from "react-native";
import {
    AreaChart,
    Grid,
    StackedAreaChart,
    BarChart,
    LineChart,
    PieChart,
    ProgressCircle,
    YAxis,
    XAxis
} from ''react-native-svg-charts'';
import * as shape from ''d3-shape''

const ReactNativeSvgCharts = () => {
    React.useEffect(() => {

    }, []);
    return <SafeAreaView style={{flex: 1}}>
        <ScrollView>
            <AreaChart
                style={{height: 200}}
                data={[50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]}
                contentInset={{top: 30, bottom: 30}}
                curve={shape.curveNatural}
                svg={{fill: ''rgba(134, 65, 244, 0.8)''}}
            >
                <Grid/>
            </AreaChart>
            <StackedAreaChart
                style={{height: 200, paddingVertical: 16}}
                data={[
                    {
                        month: new Date(2015, 0, 1),
                        apples: 3840,
                        bananas: 1920,
                        cherries: 960,
                        dates: 400,
                    },
                    {
                        month: new Date(2015, 1, 1),
                        apples: 1600,
                        bananas: 1440,
                        cherries: 960,
                        dates: 400,
                    },
                    {
                        month: new Date(2015, 2, 1),
                        apples: 640,
                        bananas: 960,
                        cherries: 3640,
                        dates: 400,
                    },
                    {
                        month: new Date(2015, 3, 1),
                        apples: 3320,
                        bananas: 480,
                        cherries: 640,
                        dates: 400,
                    },
                ]}
                keys={[''apples'', ''bananas'', ''cherries'', ''dates'']}
                colors={[''#8800cc'', ''#aa00ff'', ''#cc66ff'', ''#eeccff'']}
                curve={shape.curveNatural}
                showGrid={false}
                svgs={[
                    {onPress: () => console.log(''apples'')},
                    {onPress: () => console.log(''bananas'')},
                    {onPress: () => console.log(''cherries'')},
                    {onPress: () => console.log(''dates'')},
                ]}
            />
            <BarChart
                style={{height: 200}}
                data={[50, 10, 40, 95, -4, -24, null, 85, undefined, 0, 35, 53, -53, 24, 50, -20, -80]}
                svg={{fill: ''rgb(134, 65, 244)''}}
                contentInset={{top: 30, bottom: 30}}
            >
                <Grid/>
            </BarChart>

            <LineChart
                style={{height: 200}}
                data={[50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]}
                svg={{stroke: ''rgb(134, 65, 244)''}}
                contentInset={{top: 20, bottom: 20}}
            >
                <Grid/>
            </LineChart>

            <PieChart
                style={{height: 200}}
                data={
                    [50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80].filter((value) => value > 0)
                        .map((value, index) => ({
                            value,
                            svg: {
                                fill: (''#'' + ((Math.random() * 0xffffff) << 0).toString(16) + ''000000'').slice(0, 7),
                                onPress: () => console.log(''press'', index),
                            },
                            key: `pie-${index}`,
                        }))
                }
            />

            <ProgressCircle
                style={{height: 200}}
                progress={0.7}
                progressColor={''rgb(134, 65, 244)''}
            />

            <View style={{height: 200, flexDirection: ''row''}}>
                <YAxis
                    data={[50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]}
                    contentInset={{top: 20, bottom: 20}}
                    svg={{
                        fill: ''grey'',
                        fontSize: 10,
                    }}
                    numberOfTicks={10}
                    formatLabel={(value) => `${value}ºC`}
                />
                <LineChart
                    style={{flex: 1, marginLeft: 16}}
                    data={[50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]}
                    svg={{stroke: ''rgb(134, 65, 244)''}}
                    contentInset={{top: 20, bottom: 20}}
                >
                    <Grid/>
                </LineChart>
            </View>

            <View style={{height: 200, padding: 20}}>
                <LineChart
                    style={{flex: 1}}
                    data={[50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]}
                    gridMin={0}
                    contentInset={{top: 10, bottom: 10}}
                    svg={{stroke: ''rgb(134, 65, 244)''}}
                >
                    <Grid/>
                </LineChart>
                <XAxis
                    style={{marginHorizontal: -10}}
                    data={[50, 10, 40, 95, -4, -24, 85, 91, 35, 53, -53, 24, 50, -20, -80]}
                    formatLabel={(value, index) => index}
                    contentInset={{left: 10, right: 10}}
                    svg={{fontSize: 10, fill: ''black''}}
                />
            </View>
        </ScrollView>
    </SafeAreaView>;
};

export default ReactNativeSvgCharts;

C6DEEC18-4E4E-47F0-84B7-3277296EAACB-42276-00004CE9927F21F8.GIF

react-native-progress-bar进度条

import React, {FC} from "react";
import {StyleSheet, Animated, Easing, View, ViewStyle} from "react-native";


interface Props {
    initialProgress?: number;
    progress: number;
    easing?: (value: number) => number;
    easingDuration?: number;
    backgroundStyle?: ViewStyle;
    style?: ViewStyle;
    fillStyle?: ViewStyle;
}

const ProgressBar: FC<Props> = (props) => {
    const progressRef = React.useRef(new Animated.Value(props.initialProgress || 0));

    React.useEffect(() => {
        update();
    }, [props.progress]);

    const update = () => {
        Animated.timing(progressRef.current, {
            useNativeDriver: false,
            easing: props.easing!,
            duration: props.easingDuration!,
            toValue: props.progress
        }).start();
    };


    React.useEffect(() => {

    }, []);

    return <View style={[styles.background, props.backgroundStyle, props.style]}>
        <Animated.View style={[styles.fill, props.fillStyle, {
            width: progressRef.current.interpolate({
                inputRange: [0, 1],
                outputRange: [0, 1 * (props.style!.width || 0)],
            })
        }]}/>
    </View>;
};

const styles = StyleSheet.create({
    background: {
        backgroundColor: ''#bbbbbb'',
        height: 5,
        overflow: ''hidden''
    },
    fill: {
        backgroundColor: ''#3b5998'',
        height: 5
    }
});

ProgressBar.defaultProps = {
    initialProgress: 0,
    easingDuration: 500,
    easing: Easing.inOut(Easing.ease),
    backgroundStyle: {},
    style: {},
    fillStyle: {}
}

export default ProgressBar;
<ProgressBar
    fillStyle={{}}
    backgroundStyle={{backgroundColor: ''#cccccc'', borderRadius: 2}}
    style={{marginTop: 10, width: 300}}
    progress={progress}
/>

957E4EAA-3FAF-4030-B415-68DFF9DED649-72122-00004E1A6BF907FF.GIF

react-native-gesture-password手势密码

import React from "react";
import {SafeAreaView, ScrollView} from "react-native";
import GesturePassword from "react-native-gesture-password";


const ReactNativeGesturePassword = () => {
    const [status, setStatus] = React.useState(''normal'');
    const [message, setMessage] = React.useState(''请输入密码'');
    const refPassword = React.useRef<GesturePassword>();

    React.useEffect(() => {

    }, []);

    const onStart = () => {

    }

    const onEnd = (password: string) => {
        refPassword.current!.resetActive();
    }
    return <SafeAreaView>
        <GesturePassword
            ref={(e) => refPassword.current = e}
            status={status}
            message={message}
            onStart={() => onStart()}
            onEnd={(password) => onEnd(password)}
            innerCircle={true}
            outerCircle={true}
        />
    </SafeAreaView>;
};

export default ReactNativeGesturePassword;

github

持续更新...
IMG_9163.JPG

React Native 基于react-native-camera实现扫码功能

React Native 基于react-native-camera实现扫码功能

组件库文档: https://github.com/react-nati...

安装

  1. npm install react-native-camera --save
  2. react-native link react-native-camera
使用最新的稳定版,将你的package.json这样配置:
"react-native-camera": "git+https://git@github.com/react-native-community/react-native-camera"

配置(IOS)

如果上一步的link显示成功,则xcode里的配置基本完成,如果失败,可以手动配置;

手动配置

1.你已经完成 npm install react-native-camera --save

  1. 在XCode中,在项目导航器中右键单击Libraries➜Add Files to [your project's name];
  2. 转到node_modules➜ react-native-camera并添加RNCamera.xcodeproj;
  3. 展开RNCamera.xcodeproj➜ Products文件夹;
  4. 在XCode中,在项目导航器中选择您的项目。添加libRNCamera.a到您的项目Build Phases➜Link Binary With Libraries;

  1. 点击RNCamera.xcodeproj项目导航器并转到Build Settings选项卡。确保“All”开启(而不是'Basic')。在该Search Paths部分中,查找Header Search Paths并确保它包含两者$(SRCROOT)/../../react-native/React并将它们$(SRCROOT)/../../../React标记为recursive

使用

只需import { RNCamera } from react-native-camera 模块中 取出<RNCamera/>标签。

引用标签:

<RNCamera
         ref={ref => {
             this.camera = ref;
         }}
         style={styles.preview}
         type={RNCamera.Constants.Type.back}
         flashMode={RNCamera.Constants.FlashMode.on}
         onBarCodeRead={this.onBarCodeRead}
         >  
</RNCamera>

属性

autoFocus

值:(RNCamera.Constants.AutoFocus.on默认)或RNCamera.Constants.AutoFocus.off

使用该autoFocus属性指定相机的自动对焦设置。
RNCamera.Constants.AutoFocus.on将其打开,RNCamera.Constants.AutoFocus.off将其关闭。

flashMode

指定相机的闪光模式
值:(RNCamera.Constants.FlashMode.off默认)RNCamera.Constants.FlashMode.on

onBarCodeRead

检测到条形码时,将调用指定的方法;
事件包含data(条形码中的数据)和type(检测到的条形码类型)

绘制扫码界面

代码:

render() {
       return (
           <View style={styles.container}>
               <RNCamera
                   ref={ref => {
                       this.camera = ref;
                   }}
                   style={styles.preview}
                   type={RNCamera.Constants.Type.back}
                   flashMode={RNCamera.Constants.FlashMode.on}
                   onBarCodeRead={this.onBarCodeRead}
               >
                   <View style={styles.rectangleContainer}>
                       <View style={styles.rectangle}/>
                       <Animated.View style={[
                           styles.border,{transform: [{translateY: this.state.moveAnim}]}]}/>
                       <Text style={styles.rectangleText}>将二维码放入框内,即可自动扫描</Text>
                   </View>
                   </RNCamera>
           </View>
       );
   }

在 Camera 组件中绘制一个绿色的正方形 View,随后就是绘制绿色方框中滚动的线。使用 RN 中的 Animated 组件来实现动画效果。 首先在 componentDidMount 函数中初始化动画函数。

componentDidMount() {
    this.startAnimation();
}

startAnimation = () => {
    this.state.moveAnim.setValue(0);
    Animated.timing(
        this.state.moveAnim,{
            tovalue: -200,duration: 1500,easing: Easing.linear
        }
    ).start(() => this.startAnimation());
};

并且记得在构造函数中初始化 state:

constructor(props) {
    super(props);
    this.state = {
        moveAnim: new Animated.Value(0)
    };
}

通过 onBarCodeRead 函数来处理扫描结果:

//  识别二维码
    onBarCodeRead = (result) => {
        const { navigate } = this.props.navigation;
               const {data} = result; //只要拿到data就可以了
               //路由跳转到webView页面;
            navigate('Sale',{ 
                url: data
            })
    };

完整版示例:

class ScanScreen extends Component {
    constructor(props) {
        super(props);
        this.state = {
            moveAnim: new Animated.Value(0)
        };
    }

    componentDidMount() {
        this.startAnimation();
    }

    startAnimation = () => {
        this.state.moveAnim.setValue(0);
        Animated.timing(
            this.state.moveAnim,{
                tovalue: -200,easing: Easing.linear
            }
        ).start(() => this.startAnimation());
    };
    //  识别二维码
    onBarCodeRead = (result) => {
        const { navigate } = this.props.navigation;
               const {data} = result;
            navigate('Sale',{
                url: data
            })
    };

    render() {
        return (
            <View style={styles.container}>
                <RNCamera
                    ref={ref => {
                        this.camera = ref;
                    }}
                    style={styles.preview}
                    type={RNCamera.Constants.Type.back}
                    flashMode={RNCamera.Constants.FlashMode.on}
                    onBarCodeRead={this.onBarCodeRead}
                >
                    <View style={styles.rectangleContainer}>
                        <View style={styles.rectangle}/>
                        <Animated.View style={[
                            styles.border,{transform: [{translateY: this.state.moveAnim}]}]}/>
                        <Text style={styles.rectangleText}>将二维码放入框内,即可自动扫描</Text>
                    </View>
                    </RNCamera>
            </View>
        );
    }
}

export default ScanScreen;

const styles = StyleSheet.create({
    container: {
        flex: 1,flexDirection: 'row'
    },preview: {
        flex: 1,justifyContent: 'flex-end',alignItems: 'center'
    },rectangleContainer: {
        flex: 1,alignItems: 'center',justifyContent: 'center',backgroundColor: 'transparent'
    },rectangle: {
        height: 200,width: 200,borderWidth: 1,borderColor: '#00FF00',rectangleText: {
        flex: 0,color: '#fff',marginTop: 10
    },border: {
        flex: 0,height: 2,backgroundColor: '#00FF00',}
});

今天关于ReactNative: 了解相机第三方库 react-native-camera 的使用react native cameraroll的分享就到这里,希望大家有所收获,若想了解更多关于RactNative---react-native-storage使用、React Native -19.React Native Timer定时器的使用、React Native App第三方库、React Native 基于react-native-camera实现扫码功能等相关知识,可以在本站进行查询。

本文标签: