You need to enable JavaScript to run this app. 如何在Expo Camera中获取当前视频帧且无需使用takePicture方法
优惠活动
大模型
产品
解决方案
定价
更多

如何在Expo Camera中获取当前视频帧且无需使用takePicture方法

问题原因

Expo Camera 是React Native环境下的原生封装组件,不存在Web DOM中的video属性,因此你访问cameraRef.current.video会返回undefined,无法直接复用Web端react-webcam的写法。

不使用takePictureAsync获取当前视频帧的实现方案

你可以选择以下两种方案实现需求,均不需要调用系统拍照接口:

方案1:定时截取预览帧(适配你现有500ms轮询的逻辑)

使用Expo Camera提供的takeSnapshotAsync方法,该方法直接截取当前相机预览流的帧,不会触发系统拍照逻辑、没有快门声,性能远高于takePictureAsync,和你Web端获取video元素帧的逻辑一致。
示例代码如下:

import React, { useState, useEffect, useRef } from "react";
import { Camera } from "expo-camera";
import { Platform } from 'react-native';

export function MyFunc () {
  const cameraRef = useRef(null);
  const [hasPermission, setHasPermission] = useState(null);

  // 申请相机权限
  useEffect(() => {
    (async () => {
      const { status } = await Camera.requestCameraPermissionsAsync();
      setHasPermission(status === 'granted');
    })();
  }, []);

  const processFrame = async () => {
    if (cameraRef.current) {
      // 截取当前预览帧,可自定义参数调整输出格式、质量、尺寸
      const frame = await cameraRef.current.takeSnapshotAsync({
        quality: 0.5, // 质量范围0-1,按需调整
        base64: false, // 不需要base64可关闭,提升性能
        skipProcessing: Platform.OS === 'android', // 安卓端跳过额外处理提升速度
      });
      // frame.uri为当前帧的本地路径,也可获取base64或原始数据做后续处理
      console.log(frame);
    }
    setTimeout(() => processFrame(), 500)
  };

  useEffect(() => {
    if (hasPermission) {
      processFrame();
    }
  }, [hasPermission]);

  if (hasPermission === null) return <></>;
  if (hasPermission === false) return <>没有相机权限</>;

  return (
    <Camera
      ref={cameraRef}
      type={Camera.Constants.Type.front}
      style={{opacity: 0, width:1, height:1}}
    />
  );
};

方案2:实时逐帧监听

如果你需要连续处理每一帧而不是间隔取帧,可以开启预览帧监听,直接获取每帧的原始像素数据:

import React, { useState, useEffect, useRef } from "react";
import { Camera } from "expo-camera";

export function MyFunc () {
  const cameraRef = useRef(null);
  const [hasPermission, setHasPermission] = useState(null);

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

  useEffect(() => {
    if (!hasPermission || !cameraRef.current) return;

    // 注册预览帧监听
    const listener = Camera.addPreviewFrameListener((frame) => {
      // frame包含width、height、bytes(原始像素Uint8Array数据)
      // 在这里直接处理每帧数据即可
      console.log(`获取到帧,尺寸:${frame.width}*${frame.height}`);
    });

    // 启动帧观测
    cameraRef.current.startObservingPreviewFrames({
      format: Camera.PreviewFormat.RGBA_8888, // 输出像素格式按需调整
    });

    return () => {
      // 卸载时关闭监听避免内存泄漏
      listener.remove();
      cameraRef.current?.stopObservingPreviewFrames();
    }
  }, [hasPermission]);

  if (hasPermission === null) return <></>;
  if (hasPermission === false) return <>没有相机权限</>;

  return (
    <Camera
      ref={cameraRef}
      type={Camera.Constants.Type.front}
      style={{opacity: 0, width:1, height:1}}
    />
  );
}
注意事项
  • 两种方案都需要先获取相机权限,否则无法正常调用相机能力
  • 安卓端如果出现性能问题,可以将takeSnapshotAsync的skipProcessing参数设为true,或者降低预览帧的输出分辨率
  • 预览帧监听的回调频率较高,如果不需要实时处理,优先选择方案1的定时截取逻辑,性能开销更低

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.19 15:45:04