更新记录
1.1(2026-01-21) 下载此版本
v1.1 增加调用原生插件的 getCameraList 方法获取摄像头列表,nvue相机区域可根据id显示不同的摄像头区域
1.0(2025-09-08) 下载此版本
自定义安卓端相机插件,再Nvue中使用,可配合subNvue实现再vue中自定义区域显示相机区域。
平台兼容性
uni-app(4.07)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | - | - | √ | √ | 6.0 | - | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| × | × | √ |
LDEpiiCamera
2025.09.08 更新内容
Android DCamera 是个人自定义 安卓端 nVue、subNvue 页面 开发的原生相机插件。支持相机自动初始化、摄像头默认为前置摄像头(可联系自定义配置)、拍照、录像、缩放控制及横竖屏切换,适配 Android 6.0+ 系统,提供稳定的相机操作体验。
特色和优势
1、支持vue2、vue3
2、支持nvue中使用,也可通过app-vue配合subNvue使用,如需代码示例可联系坐着QQ
注意
manifest.json 中 App常用其他设置 minSdkVersion设置为23
只需要将插件加入到页面,然后云打包或打包自定义基座
(注意:原生插件必须要在nvue的页面,nvue和vue基本功能一直,只是存在一些差异,比如css的用法等)
调用开放的API接口(拍照、开始/停止录像、设置摄像区域缩放值),再插件可自定义宽高相机初始化默认显示适配的分辨率比例,可提供参数进行默认横竖屏预览相机区域。
、核心功能清单
✅ 相机自动初始化,无需手动调用 open/close 方法(如需要手动控制可联系作者调整)
✅ 拍照功能,自动保存至本地相册并返回路径
✅ 视频录制(支持开始 / 停止回调,返回视频路径)
✅ 0-1 范围缩放控制 this.$refs.cameraRef.setZoomValue(0-1);
✅ 横竖屏动态切换(竖屏 portrait / 横屏 landscape)
✅ 完善的状态监听(相机就绪、错误、拍照 / 录像完成)
✅ 优化资源管理,可解决多次进入vue页面(subNvue加载相机组件预览问题)
、基础使用示例
四、基础使用示例(nvue 页面、如需subNvue请自行查看相关文档或者联系作者QQ) 模板结构(Camera.nvue)
<template>
<view class="main">
<!-- 原生相机组件:支持动态切换摄像头 -->
<DCamera
:cameraId="0"
ref="cameraRef"
:style="{width:width + 'px',height:height + 'px'}"
class="camera-view"
:zoom="currentZoom"
screenOrientation="landscape"
@cameraReady="handleCameraReady"
@cameraError="handleCameraError"
@pictureTaken="handlePictureTaken"
@recordStart="handleRecordStart"
@recordEnd="handleRecordEnd"
/>
<!-- 功能按钮区域:新增【切换摄像头】按钮 -->
<!-- <view class="btn-group">
<button @click="getAllCameraList" class="btn">获取所有摄像头列表</button>
<button @click="handleSwitchCamera" class="btn" :disabled="!cameraReady || cameraCount <= 1">
切换摄像头(当前:{{selectedCameraId === 0 ? '后置' : '前置'}})
</button>
<button @click="handleTakePhoto" class="btn" :disabled="!cameraReady">拍照</button>
<button @click="handleToggleRecord" class="btn" :disabled="!cameraReady" :style="isRecording ? {background: '#ff3b30'} : {background: '#007aff'}">
{{ isRecording ? '停止录制' : '开始录制' }}
</button>
</view> -->
<!-- <view class="flex_col">
<view class="camera-list" v-if="cameraList.length > 0">
<text class="title">系统摄像头列表(共 {{cameraCount}} 个)</text>
<view class="camera-item" v-for="(item, index) in cameraList" :key="index" @click="selectCamera(item.cameraId)">
<text>ID:{{item.cameraId}} | 名称:{{item.name}} | 类型:{{item.facing}}</text>
</view>
</view>
<view class="status-info">
<text>当前选中摄像头ID:{{selectedCameraId}}</text>
<text>当前摄像头类型:{{selectedCameraId === 0 ? '后置摄像头' : '前置摄像头'}}</text>
<text>相机状态:{{cameraReady ? '已就绪' : '未就绪'}}</text>
<text>录制状态:{{isRecording ? '正在录制' : '未录制'}}</text>
<text>文件路径:{{fileUrl || '无'}}</text>
</view>
</view> -->
</view>
</template>
<script>
export default {
data() {
return {
height: 365,
width: 490,
currentZoom: 0,
selectedCameraId: 0, // 默认选中第一个摄像头(后置,ID=0)
cameraCount: 0, // 摄像头总数
cameraList: [], // 摄像头列表
cameraReady: false, // 相机就绪状态
isRecording: false, // 录制状态
fileUrl: '' // 拍照/录像文件路径
}
},
onLoad() {
// 建立通信
uni.$on('updateContainerRect', (data) => {
const { type } = data;
if (type === 'init') {
this.width = data.width;
this.height = data.height;
this.cameraReady = true;
// this.$nextTick(() => {
// this.getAllCameraList();
// });
} else if (type === 'startRecord') {
this.handleToggleRecord()
} else if (type === 'stopRecord') {
this.handleToggleRecord()
}
})
},
beforeDestroy() {
uni.$off('updateContainerRect');
// 页面销毁时停止录制并释放资源
if (this.isRecording && this.$refs.cameraRef) {
this.$refs.cameraRef.stopRecording();
}
this.cameraReady = false;
this.isRecording = false;
},
methods: {
// 1. 获取系统所有摄像头列表(先获取列表,才能准确切换)
getAllCameraList() {
// 调用原生插件的 getCameraList 方法
this.$refs.cameraRef.getCameraList((res) => {
if (res.success) {
console.log("获取摄像头列表成功", res);
this.cameraCount = res.cameraCount;
this.cameraList = res.cameraList;
uni.showToast({
title: `共获取 ${res.cameraCount} 个摄像头`,
icon: 'success'
});
} else {
console.error("获取摄像头列表失败", res);
uni.showToast({
title: res.error,
icon: 'none'
});
}
});
},
// 一键切换摄像头
handleSwitchCamera() {
// 先判断摄像头总数,若只有1个,无法切换
if (this.cameraCount <= 1) {
uni.showToast({
title: '当前设备仅支持1个摄像头',
icon: 'none'
});
return;
}
// 切换逻辑:循环切换(0 → 1 → 0 / 支持多摄像头:0→1→2→0)
let newCameraId = this.selectedCameraId + 1;
if (newCameraId >= this.cameraCount) {
newCameraId = 0; // 超出总数,回到第一个摄像头
}
// 赋值新的摄像头ID(原生插件会自动重新初始化相机)
this.selectedCameraId = newCameraId;
uni.showToast({
title: `已切换到${newCameraId === 0 ? '后置' : '前置'}摄像头`,
icon: 'success'
});
// 切换后,录制状态自动重置(避免跨摄像头录制异常)
if (this.isRecording) {
this.$refs.cameraRef.stopRecording();
this.isRecording = false;
}
},
// 手动选择指定摄像头(原有功能,配合列表使用)
selectCamera(cameraId) {
this.selectedCameraId = cameraId;
uni.showToast({
title: `已选中${cameraId}号摄像头`,
icon: 'success'
});
// 切换后停止录制,避免异常
if (this.isRecording) {
this.$refs.cameraRef.stopRecording();
this.isRecording = false;
}
},
// 拍照
handleTakePhoto() {
if (!this.cameraReady) {
uni.showToast({
title: '相机未就绪',
icon: 'none'
});
return;
}
this.$refs.cameraRef.takePicture((res) => {
if (res.success) {
console.log("拍照成功", res);
this.fileUrl = res.path;
// 预览图片
uni.previewImage({
urls: [this.fileUrl],
current: this.fileUrl
});
} else {
console.error("拍照失败", res);
uni.showToast({
title: res.error,
icon: 'none'
});
}
});
},
// 5. 切换录制状态(开始/停止,原有功能)
handleToggleRecord() {
if (!this.cameraReady) {
uni.showToast({
title: '摄像头相机相机未就绪',
icon: 'none'
});
return;
}
if (this.isRecording) {
// 停止录制
this.$refs.cameraRef.stopRecording((res) => {
if (res.success) {
this.fileUrl = res.path;
} else {
console.error("摄像头相机停止录制失败", res);
}
});
} else {
// 开始录制
this.$refs.cameraRef.startRecording((res) => {
if (res.success) {
this.fileUrl = res.path;
} else {
console.error("摄像头相机开始录制失败", res);
}
});
}
},
// 相机就绪回调(原有功能)
handleCameraReady(e) {
console.warn('摄像头相机已就绪', e);
this.cameraReady = true;
},
// 相机错误回调(原有功能)
handleCameraError(e) {
console.error("摄像头相机错误", e);
this.cameraReady = false;
uni.showToast({
title: `摄像头相机异常:${e.detail.error}`,
icon: 'none'
});
},
// 拍照完成回调(原有功能)
handlePictureTaken(e) {
console.log('摄像头相机拍照完成:', e.detail.path);
this.fileUrl = e.detail.path;
},
// 录制开始回调(同步状态,原有功能)
handleRecordStart(e) {
this.isRecording = true;
this.fileUrl = e.detail.path;
},
// 录制停止回调(同步状态,原有功能)
handleRecordEnd(e) {
this.isRecording = false;
this.fileUrl = e.detail.path;
// 可将视频路径传递给其他页面
uni.$emit('getVideoFile', {
path: e.detail.path,
type: 'videoUrl'
});
}
},
}
</script>
<style lang="scss">
.main {
position: absolute;
}
.flex_area {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
flex-wrap: wrap;
height: 30px;
padding-bottom: 5px;
}
.camera-view {}
// .main {
// position: absolute;
// display: flex;
// flex-direction: row;
// padding: 20px;
// }
// .btn-group {
// display: flex;
// margin-left: 10rpx;
// }
// .btn {
// width: 160rpx;
// height: 60rpx;
// line-height: 60rpx;
// text-align: center;
// color: #fff;
// margin-bottom: 10rpx;
// }
// .camera-list {
// background-color: #fff;
// padding: 10rpx;
// margin-bottom: 20rpx;
// }
// .title {
// font-size: 20px;
// font-weight: bold;
// margin-bottom: 15rpx;
// }
// .camera-item {
// font-size: 28rpx;
// padding: 10rpx 0;
// border-bottom: 1rpx solid #f5f5f5;
// }
// .status-info {
// background-color: #fff;
// padding: 20rpx;
// border-radius: 10rpx;
// display: flex;
// flex-direction: column;
// font-size: 26rpx;
// color: #333;
// }
// .camera-view {
// margin-bottom: 20rpx;
// border-radius: 10rpx;
// overflow: hidden;
// }
</style>
API 请求方法简要说明
| 插件方法 | 说明 | 默认值 | 是否必须 |
|---|---|---|---|
| getCameraList() | 获取所有摄像头列表 | 无 | 非 |
| takePicture() | 执行拍照 | 无 | 非 |
| startRecording() | 开始录像 | 无 | 非 |
| stopRecording() | 停止录像 | 无 | 非 |
| setZoomValue([]) | 图片水印配置 | 无 | 非 |
API 参数简要说明
属性名 类型 说明 可选值 默认值 cameraId Number/String 相机ID 0(必填,默认为0,最好调试时调用getCameraList获取想要展示摄像头的ID) 0 screenOrientation String 相机横竖屏配置 portrait(竖屏)、landscape(横屏) landscape zoom Number 缩放比例(0-1,0 = 无缩放) 0 ~ 1(步长建议 0.1) 0
-
getCameraList(callback) 功能:获取所有摄像头列表。(获取所有摄像头列表, 必须再组件DCamera标签内加入参数cameraId,指定选中摄像头ID) 参数: callback(res):回调函数,返回拍照结果 字段 类型 说明 res.success Boolean 是否获取系统摄像头成功 res.cameraCount String 当前系统摄像头数量(成功时返回) res.cameraList Object 摄像头列表(成功时返回) res.error String 错误信息(失败时返回) 示例:
this.$refs.cameraRef.getCameraList((res) => { console.log(res) }); -
takePicture(callback) 功能:拍摄照片并保存至设备存储。 参数: callback(res):回调函数,返回拍照结果 字段 类型 说明 res.success Boolean 是否拍照成功 res.path String 照片绝对路径(成功时返回) res.error String 错误信息(失败时返回) 示例:
this.$refs.cameraRef.takePicture((res) => { console.log(res) }); -
startRecording(callback) 功能:启动视频录制。 参数: callback(res):回调函数,返回录像启动结果 字段 类型 说明 res.success Boolean 是否启动成功 res.path String 视频保存路径(成功时返回) res.error String 错误信息(失败时返回) 示例:
this.$refs.cameraRef.startRecording((res) => { console.log(res) }); -
stopRecording(callback) 功能:停止视频录制并保存文件。 参数: callback(res):回调函数,返回录像停止结果 字段 类型 说明 res.success Boolean 是否停止成功 res.path String 视频绝对路径(成功时返回) res.error String 错误信息(失败时返回) 示例:
this.$refs.cameraRef.startRecording((res) => { console.log(res) }); -
setZoomValue(zoom, callback) 功能:手动调整相机缩放比例(优先级高于 zoom 属性)。 参数: zoom:Number,缩放比例(0-1,0 = 无缩放,1 = 最大缩放) callback(res):回调函数,返回缩放结果 字段 类型 说明 res.success Boolean 是否缩放成功 res.currentZoom Number 当前实际缩放比例 res.error String 错误信息(失败时返回) 示例:
// 设置缩放为50% currentZoom: 0-1 this.currentZoom = 0.5; this.$refs.cameraRef.setZoomValue(this.currentZoom);
API 回调方法监听简要说明
| 回调函数 | 说明 | 默认值 | 是否必须 |
|---|---|---|---|
| @cameraReady | 相机初始化完成并开始预览时 { status: "ready" } | 无 | 非 |
| @cameraError | 相机初始化/操作失败时 { error: "具体错误信息" } | 无 | 非 |
| @pictureTaken | 拍照完成时 { path: "照片保存路径" } | 无 | 非 |
| @recordStart | 录像启动时 { status: "started", path: "视频路径" } | 无 | 非通过组件绑定 @事件名="处理函数" 监听相机状态变化,事件与方法回调同步触发。 |
| @recordEnd | 录像停止并保存完成时 { status: "stopped", path: "视频路径" } | 无 | 非通过组件绑定 @事件名="处理函数" 监听相机状态变化,事件与方法回调同步触发。 |

收藏人数:
下载插件并导入HBuilderX
下载插件ZIP
赞赏(3)
下载 277
赞赏 3
下载 13671533
赞赏 1851
赞赏
京公网安备:11010802035340号