今天主要做了一个demo,功能很简单,就是调用手机摄像头画面,并且可以通过按钮控制拍照以及将图片保存到手机相册的功能,接下来我将从创建项目开始一步一步完成这个demo,各位只需要复制粘贴即可
创建React Native项目
npx react-native init yx_rnDemo --version 0.70.6 // 这里我使用的RN版本为0.70.6,建议各位和我一样,因为RN贼恶心了,好多插件都会因为版本问题各种报错
安装依赖
package.json所需依赖包
// react-native-vision-camera版本号就写我这个,不然也容易有问题,今天因为这个费了不少时间
"@react-native-camera-roll/camera-roll": "^7.4.2", // 照片保存到相册所需的插件
"react-native-vision-camera": "2.15.4" // 调用摄像头画面和拍照所需插件
安装命令如下
yarn add @react-native-camera-roll/camera-roll
yarn add react-native-vision-camera@2.15.4
// 后续发现个问题,安装了react-native-vision-camera@2.15.4之后,后续安装的大部分包后项目都跑不起来,大概率都跟这个插件有关,解决方法就是安装新插件之前把这个插件卸载了,等安装完新插件后先运行项目跑起来后再次重新安装react-native-vision-camera@2.15.4这个插件,具体原因我也不知道,但是我就是这样解决的
安卓环境配置
找到android/app/src/mian/AndroidManifest.xml这个文件,在manifest这个标签内添加如下请求权限的代码
<manifest>
...
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" /> // 相机权限
<uses-permission android:name="android.permission.RECORD_AUDIO" /> // 麦克风权限(单纯的拍照其实不用给)
<application
android:requestLegacyExternalStorage="true" // 添加这个(用于确保你的应用在 Android 10 中仍然能够访问外部存储,但如果你的应用目标是 Android 11 或更高版本,你应该考虑调整应用以符合 Scoped Storage 的要求。)
...
>
</application>
...
</manifest>
苹果ios环境配置
找到ios/项目名/Info.plist这个文件,在plist标签内新增如下代码
<pilst>
...(其他代码)
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your Camera.</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your Microphone.</string>
...(其他代码)
</plist>
接下来就是在单文件里面直接写代码了
找到根目录下的App.js(主入口文件),将一下代码直接粘贴进去即可
import { useCameraDevices, Camera } from 'react-native-vision-camera'
import * as React from 'react'
import { useRef, useEffect, useState } from 'react'
import { PermissionsAndroid, Text, View, Button, Alert, Platform } from 'react-native'
import { CameraRoll } from "@react-native-camera-roll/camera-roll"
export default function CameraDemo (props) {
const cameraRef = useRef(null) // 创建一个 ref 来引用相机组件
const [hasCameraPermission, setHasCameraPermission] = useState(false) // 状态用于跟踪摄像头权限
const [currentDevice, setCurrentDevice] = useState(null) // 跟踪当前摄像头设备
useEffect(async () => {
// 在组件加载时请求相机权限
async function requestCameraPermission () {
try {
// 请求相机权限
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
title: 'Camera Permission',
message: 'App needs access to your camera.',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
)
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('Camera permission granted')
setHasCameraPermission(true) // 如果权限被授予,设置相机权限状态为 true
} else {
console.log('Camera permission denied')
setHasCameraPermission(false) // 如果权限被拒绝,设置相机权限状态为 false
}
} catch (err) {
console.warn(err)
}
}
async function requestStoragePermission () {
try {
const granted = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
])
if (
granted['android.permission.WRITE_EXTERNAL_STORAGE'] === PermissionsAndroid.RESULTS.GRANTED &&
granted['android.permission.READ_EXTERNAL_STORAGE'] === PermissionsAndroid.RESULTS.GRANTED
) {
console.log('Storage permissions granted')
// 在此处执行您需要的操作,比如保存照片到相册
} else {
console.log('Storage permissions denied')
// 处理权限被拒绝的情况
}
} catch (err) {
console.warn(err)
}
}
await requestCameraPermission() // 调用函数以请求相机权限
await requestStoragePermission() // 请求图库权限
}, [])
const devices = useCameraDevices()
// 设置默认摄像头(默认后置,如果想设置的哪个后面进来还是哪个的话可以自行再做处理)
useEffect(() => {
if (devices.back) {
setCurrentDevice(devices.back)
}
}, [devices])
// 切换摄像头
const toggleCamera = () => {
setCurrentDevice(currentDevice === devices.back ? devices.front : devices.back)
}
const saveToCameraRoll = async (photo) => {
const path = Platform.OS === 'android' ? 'file://' + photo.path : photo.path
// 使用 CameraRoll 保存图片到相册
CameraRoll.saveToCameraRoll(path, 'photo')
.then(() => {
Alert.alert('Success', '图片到相册保存成功')
})
.catch((error) => {
Alert.alert('Error', '图片保存到相册失败')
})
}
const takePhoto = async () => {
if (cameraRef.current) {
try {
const photo = await cameraRef.current.takePhoto()
console.log('Photo taken:', photo)
photo.id = new Date().getTime().toString() // 添加一个唯一的 id 属性
saveToCameraRoll(photo) // 将路径传递过去,为了将照片存入相册(默认不是存到相册里面)
} catch (error) {
console.error('Error taking photo:', error)
}
}
}
return (
<View style={{ flex: 1 }}>
{hasCameraPermission && currentDevice && (
<View>
<Camera
ref={cameraRef} // 绑定ref
style={{ width: 200, height: 300 }}
device={currentDevice} // 绑定设备
isActive={true}
photo={true} // 打开相机功能
frameProcessorFps={'auto'}
/>
<Button title="拍照" onPress={takePhoto} />
<Button title="切换摄像头" onPress={toggleCamera} />
</View>
)}
{!hasCameraPermission && <Text>Camera permission not granted.</Text>}
</View>
)
}
再附带一个方法,可以通过传入base64字符串图片保存到相册,支持安卓和ios
import RNFS from 'react-native-fs';
import {CameraRoll} from '@react-native-camera-roll/camera-roll';
import {Platform, PermissionsAndroid} from 'react-native';
const saveImage = async imgBase => {
if (Platform.OS === 'android') {
const storeLocation = `${RNFS.ExternalDirectoryPath}`;
const downloadDest = `${storeLocation}/${new Date().getTime()}qr.png`;
const imageData = imgBase.split('data:image/png;base64,')[1];
try {
// 注:必须写以下PermissionsAndroid.requestMultiple,请求权限,否则逻辑会走入Toast.show("保存失败!", e);并且访问权限的操作必须在writeFile之前!!!
await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
]);
await RNFS.writeFile(downloadDest, imageData, 'base64');
await CameraRoll.saveToCameraRoll('file://' + downloadDest);
} catch (e) {
console.log('err', e);
}
} else {
// IOS使用已知base64编码的图片,保存到相册
const storeLocation = `${RNFS.DocumentDirectoryPath}`;
const downloadDest = `${storeLocation}/${new Date().getTime()}qr.png`;
const imageData = imgBase.split('data:image/png;base64,')[1];
try {
await RNFS.writeFile(downloadDest, imageData, 'base64');
var promise = CameraRoll.saveAsset(downloadDest, {type: 'photo'});
promise.then(function (result) {}).catch(function (error) {});
} catch (err) {
console.log('err', e);
}
}
};
export {saveImage};
使用方式
import {saveImage} from '../tool/FileHelper';
...
saveImage('base64图片')
...