更新记录

1.0.0(2026-02-24)

自定义相机


平台兼容性

uni-app(4.57)

Vue2 Vue2插件版本 Vue3 Vue3插件版本 Chrome Safari app-vue app-nvue app-nvue插件版本 Android Android插件版本 iOS 鸿蒙
1.0.0 1.0.0 × × × 1.0.0 10.0 1.0.0 × ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × - × ×

uni-app x(4.57)

Chrome Safari Android Android插件版本 iOS 鸿蒙 微信小程序
× × 10.0 1.0.0 × × ×

插件地址https://ext.dcloud.net.cn/plugin?id=26956

使用说明

导入示例项目制作自定义基座 运行到安卓手机或者模拟器

购买源码版直接修改kotlin代码,很大提高了扩展性。 写本插件的初衷是现有的相机插件没有一个能完全满足我的需要,尝试了很多插件要么不能调分辨率,要么不缩放等,所以结合ai和自学uts插件和kotlin写了一个,可能不是很完善。 第一次尝试安卓uts原生插件,可能存在一些问题,请及时反馈 如需更多功能购买源码版自行修改,比如清晰度分辨率保存的地址很简单改个参数就行了。

目前主要实现功能

  1. 拍照
  2. 录像
  3. 切换摄像头
  4. 缩放
  5. 预览

插件没有获取权限需要在你项目获取相机相册麦克风权限参考示例项目 安卓模块配置需要勾上相机和录音 使用示例

<template>
  <view class="ima-camera" style="" :style="{ width: screen.windowWidth+1, height: screen.windowHeight+1 }">
    <my-camera class="CameraPreview" ref="cameraView" 
    @returNevent="returNevent"
    @errorEvent="errorEvent"
    :style="`height: ${screen.windowWidth*1.777}px;width:${screen.windowWidth}px`"></my-camera>
    <cover-view class="camera-timer">
        <text class="timer">{{ videTimer }}</text>
    </cover-view>
    <view v-if="!videoIng" @tap="flip" class=" flip" >
        <text style="color: #ffffff;">切换摄像头</text>
    </view>
     <view class="camera-menu" style="width: 200px;">
        <view class=" " >
            <slider block-size="28" @changing="slider" :value="sliderval" :max="1" :min="0" :step="0.01"></slider>
        </view>
        <view class=" ">
            <button  @tap="photograph" style="color:#ffffff;backgroundColor:#32b16c;borderColor:#32b16c">拍照</button>
            <button  v-if="videoIng" @tap="endVideo" style="color:#ffffff;backgroundColor:#32b16c;borderColor:#32b16c">结束录制</button>
            <button v-else @tap="startVideo" style="color:#ffffff;backgroundColor:#e5004f;borderColor:#e5004f">开始录制</button>
        </view>
    </view>
  </view>
</template>
<script setup>
    import {onLoad,onReady,onUnload, } from "@dcloudio/uni-app";
    import {ref,getCurrentInstance} from 'vue'

    const { windowWidth, windowHeight } = uni.getSystemInfoSync();
    const screen=ref({
        windowWidth:windowWidth,
        windowHeight:windowHeight
    })
    console.log('宽高',windowWidth,windowHeight)
    const cameraView=ref(null)
    const videoIng=ref(false)
    const sliderval=ref(0)
    const videTimer=ref('00:00')
    let back=true
    let timer=null
    onLoad((opt)=>{

    })
    onReady(()=>{

    })
    onUnload(()=>{

    })
    ((res)=>{
        const { windowWidth, windowHeight } = uni.getSystemInfoSync();
        screen.value.windowWidth=windowWidth
        screen.value.windowHeight=windowHeight
        // console.log('aaaa',res)
    })
    const photograph=()=>{//拍照
        cameraView.value.takePhoto()
    }
    const returNevent=(res)=>{//所有数据返回通过事件返回
        console.log('回调事件',res.detail)
        if(res?.detail?.name==='initialize'){//初始化成功
            cameraView.value.getZoomState();
        }else if(res?.detail?.name==='zoomState'){//缩放当前值 0-10
            let data=res?.detail?.data||0
            sliderval.value=Number(data.toFixed(1))
        }else if(res?.detail?.name==='switchCamera'){//切换摄像头回调
            setTimeout(()=>{
                cameraView.value.getZoomState();
            },500)
        }else if(res?.detail?.name==='startVideo'){//开始录制回调
            let seconds = 0
            videoIng.value = true
            timer = setInterval(() => {
                seconds++
                const mm = String(Math.floor(seconds / 60)).padStart(2, '0')
                const ss = String(seconds % 60).padStart(2, '0')
                videTimer.value = `${mm}:${ss}`
            }, 1000)
        }else if(res?.detail?.name==='endVideo'){//结束录制回调
            clearInterval(timer)
            videTimer.value = '00:00'
            videoIng.value = false
            uni.showModal({
                title: '录像成功',
                content: '视频地址:'+res?.detail?.data,
                success: function(res) {
                    if (res.confirm) {

                    } else if (res.cancel) {
                        console.log('用户点击取消');
                    }
                }
            });
        }else if(res?.detail?.name==='pahto'){
            uni.showModal({
                title: '拍照成功',
                content: '照片地址:'+res?.detail?.data,
                success: function(res) {
                    if (res.confirm) {

                    } else if (res.cancel) {
                        console.log('用户点击取消');
                    }
                }
            });
        }

    }
    const errorEvent=(res)=>{//错误回调事件
        const data=res.detail
        console.log('错误回调事件',data)
        uni.showModal({
            title: '提示',
            content: data.error||'错误!',
            showCancel:false,
            success: function (res) {
                if (res.confirm) {
                } else if (res.cancel) {
                }
            }
        });
    }

    const flip=()=>{//切换摄像头
        cameraView.value.switchCamera()
    }
    const startVideo=()=>{//开始录制
        cameraView.value.startRecording()
    }
    const endVideo=()=>{//结束录制
        uni.showModal({
            title: '提示',
            content: '确认停止录制!',
            success: function(res) {
                if (res.confirm) {
                    cameraView.value.stopRecording()
                } else if (res.cancel) {
                    console.log('用户点击取消');
                }
            }
        });
    }
    const slider=(val)=>{//滑动变焦
        if(sliderval.value===val.detail.value){
            return
        }
        sliderval.value=val.detail.value
        cameraView.value.setZoom(sliderval.value);
    }

    const requestAndroidPermission=(permissionID)=>{
        return new Promise((resolve, reject) => {
            plus.android.requestPermissions(
                [permissionID], 
                function(resultObj) {
                    var result = 0;
                    for (var i = 0; i < resultObj.granted.length; i++) {
                        var grantedPermission = resultObj.granted[i];
                        console.log('已获取的权限:' + grantedPermission);
                        result = 1
                    }
                    for (var i = 0; i < resultObj.deniedPresent.length; i++) {
                        var deniedPresentPermission = resultObj.deniedPresent[i];
                        console.log('拒绝本次申请的权限:' + deniedPresentPermission);
                        result = 0
                    }
                    for (var i = 0; i < resultObj.deniedAlways.length; i++) {
                        var deniedAlwaysPermission = resultObj.deniedAlways[i];
                        console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
                        result = -1
                    }
                    resolve(result);
                    // 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限
                    // if (result != 1) {
                    // gotoAppPermissionSetting()
                    // }
                },
                function(error) {
                    console.log('申请权限错误:' + error.code + " = " + error.message);
                    resolve({
                        code: error.code,
                        message: error.message
                    });
                }
            );
        });
    }
    requestAndroidPermission('android.permission.WRITE_EXTERNAL_STORAGE')
    requestAndroidPermission('android.permission.READ_EXTERNAL_STORAGE')
    requestAndroidPermission('android.permission.CAMERA')
    requestAndroidPermission('android.permission.RECORD_AUDIO')

</script>

<style lang="scss">
    .ima-camera {
        display: flex;
        justify-content: center;
        background: #000;
    }
    .CameraPreview{
    }
.flip {
    position: fixed;
    top: 90rpx;
    right: 50rpx;
}
.camera-menu {
        position: fixed;
        bottom: 30px;
        z-index: 999;
    }
.camera-timer {
        position: fixed;
        top: 100rpx;
        left: 60rpx;
        .timer {
            text-align: center;
            color: #fff;
            font-size: 26rpx;
        }
    }
</style>

隐私、权限声明

1. 本插件需要申请的系统权限列表:

<uses-feature android:name="android.hardware.camera"/> <uses-permission android:name="android.permission.WRITE_SETTINGS"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。