You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native Camera可拍照无法录视频的函数式实现问题咨询

问题修复方案

核心问题点

  • 状态类型错误:isRecording 初始值为字符串 "false",而非布尔值 false。非空字符串在布尔判断中为真值,因此 !isRecording 永远返回 false,takeVideo 内部逻辑完全不会执行,自然没有任何日志输出。
  • 权限缺失:录制视频需要同时获取相机、麦克风权限,当前仅申请了照片存储权限,缺少麦克风权限会直接导致录制失败。
  • 交互逻辑不全:仅实现了开始录制逻辑,没有手动停止录制的触发入口,虽然配置了10秒自动停止,但实际使用需要支持手动控制。

修改后代码

import { useState, useRef, useEffect } from 'react';
import { View, TouchableOpacity, Text, StyleSheet, PermissionsAndroid, Platform } from 'react-native';
import { RNCamera } from "react-native-camera";

const App = () => {
  let [flash, setFlash] = useState("off");
  let [zoom, setZoom] = useState(0);
  let [autoFocus, setAutoFocus] = useState("on");
  let [depth, setDepth] = useState(0);
  let [type, setType] = useState("back");
  let [permission, setPermission] = useState("undetermined");
  // 修正为布尔值初始值
  let [isRecording, setIsRecording] = useState(false);
  let [recordingOptions, setRecordingOptions] = useState({
    mute: false,
    maxDuration: 10,
    quality: RNCamera.Constants.VideoQuality["360p"],
  });
  let cameraRef = useRef(null);

  // 申请必要权限:相机+麦克风+存储
  const requestPermissions = async () => {
    if (Platform.OS === 'android') {
      const granted = await PermissionsAndroid.requestMultiple([
        PermissionsAndroid.PERMISSIONS.CAMERA,
        PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
        PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
      ]);
      const allGranted = Object.values(granted).every(status => status === PermissionsAndroid.RESULTS.GRANTED);
      setPermission(allGranted ? 'authorized' : 'denied');
    } else {
      // iOS权限申请逻辑可根据实际使用的权限库调整
      const cameraPermission = await Permissions.check('camera');
      const micPermission = await Permissions.check('microphone');
      setPermission(cameraPermission === 'authorized' && micPermission === 'authorized' ? 'authorized' : 'denied');
    }
  };

  useEffect(() => {
    requestPermissions();
  }, []);

  const toggleFlash = () => {
    const flashModeOrder = { off: 'on', on: 'off' };
    setFlash(flashModeOrder[flash]);
  };
  const zoomOut = () => {
    setZoom(zoom - 0.1 < 0 ? 0 : zoom - 0.1);
  };
  const zoomIn = () => {
    setZoom(zoom + 0.1 > 1 ? 1 : zoom + 0.1);
  };
  const takePicture = async () => {
    if (cameraRef.current) {
      const options = { quality: 0.5, base64: true };
      const data = await cameraRef.current.takePictureAsync(options);
      console.log('拍照结果:', data.uri);
    }
  };

  const takeVideo = async () => {
    if (cameraRef.current && !isRecording) {
      try {
        const recordoptions = {
          mute: false,
          maxDuration: 10,
          quality: RNCamera.Constants.VideoQuality["360p"],
        };
        const promise = cameraRef.current.recordAsync(recordoptions);
        if (promise) {
          setIsRecording(true);
          const data = await promise;
          console.log("录制结果:", data.uri);
          // 录制结束(自动到时长/手动停止)后重置状态
          setIsRecording(false);
        }
      } catch (e) {
        console.error('录制错误:', e);
        setIsRecording(false);
      }
    }
  };

  // 新增停止录制逻辑
  const stopVideo = () => {
    if (cameraRef.current && isRecording) {
      cameraRef.current.stopRecording();
      setIsRecording(false);
    }
  };

  // 权限未授权时展示提示
  if (permission !== 'authorized') {
    return <View style={styles.container}><Text style={{color:'#fff', fontSize:16}}>请授予相机和麦克风权限后使用</Text></View>;
  }

  return (
    <View style={styles.container}>
      <RNCamera
        ref={cameraRef}
        style={styles.preview}
        type={type}
        flashMode={flash}
        zoom={zoom}
        autoFocus={autoFocus}
      />
      <View style={{ flex: 0, flexDirection: "row", justifyContent: "center", marginBottom: 10 }}>
        <TouchableOpacity onPress={takePicture} style={styles.capture}>
          <Text style={{ fontSize: 14 }}> TAKE PICTURE </Text>
        </TouchableOpacity>
      </View>
      <View style={{ flex: 0, flexDirection: "row", justifyContent: "center" }}>
        {/* 按钮根据录制状态切换功能和文字 */}
        <TouchableOpacity onPress={isRecording ? stopVideo : takeVideo} style={styles.capture}>
          <Text style={{ fontSize: 14 }}> {isRecording ? "STOP VIDEO" : "TAKE VIDEO"} </Text>
        </TouchableOpacity>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    backgroundColor: 'black',
    justifyContent:'center',
    alignItems:'center'
  },
  preview: {
    flex: 1,
    justifyContent: 'flex-end',
    alignItems: 'center',
    width:'100%'
  },
  capture: {
    flex: 0,
    backgroundColor: '#fff',
    borderRadius: 5,
    padding: 15,
    paddingHorizontal: 20,
    alignSelf: 'center',
    margin: 20,
  }
});

export default App;

补充说明

修改后点击录制按钮会开始录制,按钮文字变为停止录制,点击即可手动结束,10秒到了也会自动结束,录制完成后会打印视频本地路径。

内容的提问来源于stack exchange,提问作者newcoder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 15:57:03