React Native Camera的新手教程

自从手机有了摄像头之后,我们可以很方便地把生活中的瞬间记录下来。我们可以通过照片和视频来回顾那些美好的瞬间。自然而然,和我们最爱的朋友和家人分享这些照片和视频就成为了一个必然的需求。

所以,在你写一个app的时候,很可能就希望他能支持拍照和拍视频的功能。假如你还不太清楚怎么来实现的,本文就是来简单介绍如何使用React Native实现相关的功能。

预先安装的包:

  • expo-camera:这个包是用来拍照和视频的。
  • Expo-av: 这个包是用来导入视频并进行播放的。

可以使用下面的命令来安装相关的包:

yarn add expo-camera expo-av

或者

npm install expo-camera expo-av

实现相机的功能

我们可以简单通过下面的代码来把camera组件假如你项目中的一个view里:

import React, { useState, useRef, useEffect } from "react";
import {
  StyleSheet,
  Dimensions,
  View,
  Text,
  TouchableOpacity,
  SafeAreaView,
} from "react-native";
import { Camera } from "expo-camera";
import { Video } from "expo-av";

const WINDOW_HEIGHT = Dimensions.get("window").height;

const closeButtonSize = Math.floor(WINDOW_HEIGHT * 0.032);
const captureSize = Math.floor(WINDOW_HEIGHT * 0.09);

export default function App() {
  const [hasPermission, setHasPermission] = useState(null);
  const [cameraType, setCameraType] = useState(Camera.Constants.Type.back);
  const [isPreview, setIsPreview] = useState(false);
  const [isCameraReady, setIsCameraReady] = useState(false);
  const [isVideoRecording, setIsVideoRecording] = useState(false);
  const [videoSource, setVideoSource] = useState(null);
  const cameraRef = useRef();

  useEffect(() => {
    (async () => {
      const { status } = await Camera.requestPermissionsAsync();
      setHasPermission(status === "granted");
    })();
  }, []);

  const onCameraReady = () => {
    setIsCameraReady(true);
  };

  const takePicture = async () => {
    if (cameraRef.current) {
      const options = { quality: 0.5, base64: true, skipProcessing: true };
      const data = await cameraRef.current.takePictureAsync(options);
      const source = data.uri;
      if (source) {
        await cameraRef.current.pausePreview();
        setIsPreview(true);
        console.log("picture source", source);
      }
    }
  };

  const recordVideo = async () => {
    if (cameraRef.current) {
      try {
        const videoRecordPromise = cameraRef.current.recordAsync();

        if (videoRecordPromise) {
          setIsVideoRecording(true);
          const data = await videoRecordPromise;
          const source = data.uri;
          if (source) {
            setIsPreview(true);
            console.log("video source", source);
            setVideoSource(source);
          }
        }
      } catch (error) {
        console.warn(error);
      }
    }
  };

  const stopVideoRecording = () => {
    if (cameraRef.current) {
      setIsPreview(false);
      setIsVideoRecording(false);
      cameraRef.current.stopRecording();
    }
  };

  const switchCamera = () => {
    if (isPreview) {
      return;
    }
    setCameraType((prevCameraType) =>
      prevCameraType === Camera.Constants.Type.back
        ? Camera.Constants.Type.front
        : Camera.Constants.Type.back
    );
  };

  const cancelPreview = async () => {
    await cameraRef.current.resumePreview();
    setIsPreview(false);
    setVideoSource(null);
  };

  const renderCancelPreviewButton = () => (
    <TouchableOpacity onPress={cancelPreview} style={styles.closeButton}>
      <View style={[styles.closeCross, { transform: [{ rotate: "45deg" }] }]} />
      <View
        style={[styles.closeCross, { transform: [{ rotate: "-45deg" }] }]}
      />
    </TouchableOpacity>
  );

  const renderVideoPlayer = () => (
    <Video
      source={{ uri: videoSource }}
      shouldPlay={true}
      style={styles.media}
    />
  );

  const renderVideoRecordIndicator = () => (
    <View style={styles.recordIndicatorContainer}>
      <View style={styles.recordDot} />
      <Text style={styles.recordTitle}>{"Recording..."}</Text>
    </View>
  );

  const renderCaptureControl = () => (
    <View style={styles.control}>
      <TouchableOpacity disabled={!isCameraReady} onPress={switchCamera}>
        <Text style={styles.text}>{"Flip"}</Text>
      </TouchableOpacity>
      <TouchableOpacity
        activeOpacity={0.7}
        disabled={!isCameraReady}
        onLongPress={recordVideo}
        onPressOut={stopVideoRecording}
        onPress={takePicture}
        style={styles.capture}
      />
    </View>
  );

  if (hasPermission === null) {
    return <View />;
  }

  if (hasPermission === false) {
    return <Text style={styles.text}>No access to camera</Text>;
  }

  return (
    <SafeAreaView style={styles.container}>
      <Camera
        ref={cameraRef}
        style={styles.container}
        type={cameraType}
        flashMode={Camera.Constants.FlashMode.on}
        onCameraReady={onCameraReady}
        onMountError={(error) => {
          console.log("cammera error", error);
        }}
      />
      <View style={styles.container}>
        {isVideoRecording && renderVideoRecordIndicator()}
        {videoSource && renderVideoPlayer()}
        {isPreview && renderCancelPreviewButton()}
        {!videoSource && !isPreview && renderCaptureControl()}
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    ...StyleSheet.absoluteFillObject,
  },
  closeButton: {
    position: "absolute",
    top: 35,
    left: 15,
    height: closeButtonSize,
    width: closeButtonSize,
    borderRadius: Math.floor(closeButtonSize / 2),
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "#c4c5c4",
    opacity: 0.7,
    zIndex: 2,
  },
  media: {
    ...StyleSheet.absoluteFillObject,
  },
  closeCross: {
    width: "68%",
    height: 1,
    backgroundColor: "black",
  },
  control: {
    position: "absolute",
    flexDirection: "row",
    bottom: 38,
    width: "100%",
    alignItems: "center",
    justifyContent: "center",
  },
  capture: {
    backgroundColor: "#f5f6f5",
    borderRadius: 5,
    height: captureSize,
    width: captureSize,
    borderRadius: Math.floor(captureSize / 2),
    marginHorizontal: 31,
  },
  recordIndicatorContainer: {
    flexDirection: "row",
    position: "absolute",
    top: 25,
    alignSelf: "center",
    justifyContent: "center",
    alignItems: "center",
    backgroundColor: "transparent",
    opacity: 0.7,
  },
  recordTitle: {
    fontSize: 14,
    color: "#ffffff",
    textAlign: "center",
  },
  recordDot: {
    borderRadius: 3,
    height: 6,
    width: 6,
    backgroundColor: "#ff0000",
    marginHorizontal: 5,
  },
  text: {
    color: "#fff",
  },
});

加入这个代码之后,应该就可以使用相机了。注意要在真实的设备上运行哦。

我们来看上面这段代码,其实实现很简单,主要分为以下几个部分:

首先来看看Camera组件的实现 <Camera …/>

我们把一个定义好的cameraRef传到了Camera组件的ref prop中。有了这个之后,就可以很方便地调用takePictureAsync(), recordAsync()。。等等。这些方法其实可以帮助我们调用相关的接口来拍照,录视频等等。

  • onCameraReady(): 这个回调是当相机准备好拍照和录视频时候的调用的。你应该注意,在我们拍照之前要先看看这个方法是不是调用了。
  • takePicture(): 通过调用takePictureAsync(),我们可以用这个方法来拍照。
  • recordVideo(): 和上面takePicture类似,通过capture组件中的onLongPress来触发,我们通过调用recordAsync来拍视频。
  • stopVideoRecording(): 这个方法在用户停止拍视频的时候触发。
  • switchCamera():这个方法会切换前后的摄像头
  • cancelPreview():这个方法就是取消照片和视频的预览。

所有的render方法,比如renderCancelPreviewButton(), renderVideoPlayer()就是简单的在你的手机屏幕上render相关的额显示。

在我们render相关的组件的时候,需要有相关的权限,我们是通过组件中的reqeustPermissionsAsync的调用来获取的。这个方法是在useEffect方法中调用的,而这个方法只有在组件初始化的时候调用。

我们也实现了一个简单的条件rendering。这里,只有在preview的情况下,我们才render一个cancel的button去取消照片和视频的preview。我们在录制的时候,才render indicator。只有录制了视频并且有视频的资源的时候,才render video播放的部分。

总结

图片和视频是非常重要的,所以让你的应用支持相关的功能是必不可少的。

参考文章: https://hackernoon.com/a-beginners-guide-to-using-react-native-camera-aj243xwn

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *