更新记录

1.0.3(2026-06-15)

  • 新增点击聚焦、双指缩放焦距、对外暴露设置焦距

1.0.2(2026-04-08)

  • 新增默认配置前后摄像头

1.0.0(2026-02-13)

  • 新版发布
查看更多

平台兼容性

uni-app(4.81)

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

uni-app x(4.81)

Chrome Safari Android iOS 鸿蒙 微信小程序
× × × ×

yt-custom-camera

特别提醒

  • 购买本插件前,请先试用,请先试用,请先试用,确认满足需求之后再行购买。虚拟物品一旦购买之后无法退款。
  • 如有使用上的疑问、bug,可以进交流群联系作者;
  • 请在合法范围内使用,若使用本插件做非法开发,本方概不负责;
  • 可扫描右侧二维码安装Android示例demo,体验插件功能
  • iOS使用示例demo测试时,要先修改manifest中项目名称后再打包(名称长度尽量短一点),不然会因为名称过长,导致App启动不了。
  • 插件需先引入再打自定义基座后使用
  • 修改插件原生代码(如 UTS / Kotlin / Swift)后,需重新制作自定义调试基座,热更新通常不生效
  • uniapp兼容式组件必须在nvue、uvue中使用才有效

插件主要功能

  • 自定义预览窗口大小(iOS、Android)
  • 拍照、录制(iOS、Android)
  • 切换摄像头、手电筒切换(iOS、Android)
  • 录制时间回调(iOS、Android)
  • 点击屏幕对焦(iOS、Android,内置手势,开箱即用)
  • 双指捏合缩放(iOS、Android,内置手势,开箱即用)
  • 编程式设置焦距 / 缩放倍率(iOS、Android,对外 API)
  • 支持uniapp、uniapp-x项目

插件接入

1.点击插件首页”试用“,选择项目导入插件,导入后可在项目根目录下uni_modules文件中查看是否有yt-custom-camera文件夹,有则导入成功。

2.由于uniapp与uniapp-x兼容式插件使用不太一样,本文档将uniapp与uniapp-x的使用分开介绍,使用者可选择对应文档对接。

使用插件

uniapp兼容式组件只能在nvue或者uvue中使用,通过标签引入,可通过css设置预览窗口的大小。

<yt-camera ref="ytcamera" style="width: 750rpx;height: 400rpx;"
            @capturePhotoHandle="capturePhotoHandle" @recordingHandle="recordingHandle"
            @recordingDurationUpdateHandler="recordingDurationUpdateHandler" cameraPosition="back"></yt-camera>

回调说明:uniapp-x项目的回调iOS与Android端有所不同具体请参照示例demo或者完整代码示例

参数 参数类型 说明
cameraPosition string 默认相机配置,后置:“back”,前置:“front”,可选配置,默认后置
capturePhotoHandle function 拍照回调,会返回拍照图片路径
recordingHandle function 录制结束回调,会返回录制视频路径
recordingDurationUpdateHandler function 录制时间回调时间格式“00:00:00"

对焦与缩放说明

内置手势(无需调用 API)

以下能力在预览区域默认开启,无需额外配置:

手势 说明
单击预览区域 点击对焦,同时调整曝光点,并显示黄色对焦框动画
双指捏合 实时改变焦距(缩放倍率),捏合放大、张开缩小

切换前后摄像头后,缩放倍率会自动重置为 1.0

焦距 / 缩放 API

如需通过代码控制焦距,可使用以下方法(iOS、Android 双端接口一致):

方法 参数 说明
setZoomFactorAction factor: number, animated: boolean 设置缩放倍率。1.0 为无缩放,数值越大画面越近(焦距越长)
getZoomFactorAction 获取当前缩放倍率(结果输出到控制台)
getMinZoomFactorAction 获取当前摄像头支持的最小缩放倍率(结果输出到控制台)
getMaxZoomFactorAction 获取当前摄像头支持的最大缩放倍率(结果输出到控制台)
resetZoomFactorAction animated: boolean 重置缩放倍率为 1.0

缩放倍率说明:

  • 1.0:默认广角,无数字变焦
  • > 1.0:放大(焦距变长,画面更近)
  • 实际生效值会自动限制在当前设备支持的 [最小倍率, 最大倍率] 范围内
  • 多数设备最小倍率为 1.0;部分多摄机型可能支持 0.5 等超广角倍率

uniapp 调用拍照、录制、切换摄像头、开关手电筒、设置焦距

uniapp开始预览,建议在onReady生命周期中调用:

onReady() {
//兼容iOS--uniapp端需要加延时
    setTimeout(() => {
        this.$refs["ytcamera"].startRunning()
    }, 300)
},
onShow() {
    if (this.$refs["ytcamera"] != null) {
        this.$refs["ytcamera"].startRunning()
    }
},
onHide() {
    if (this.$refs["ytcamera"] != null) {
        this.$refs["ytcamera"].stopRunning()
    }
},

uniapp调用拍照:

//拍照是否保存相册
let saveToAlbum = true;
this.$refs["ytcamera"].capturePhotoAction(saveToAlbum);

uniapp调用录制:

//拍照是否保存相册
//录制视频是否保存相册
let saveToAlbum = true;
//是否录制麦克风音频
let recordAudio = true;
this.$refs["ytcamera"].toggleRecordingAction(saveToAlbum, recordAudio)

uniapp调用切换摄像头:

this.$refs["ytcamera"].switchCameraAction();

uniapp调用开关手电筒:

this.$refs["ytcamera"].toggleTorchAction();

uniapp设置焦距(缩放倍率):

// 设置 2 倍缩放(带动画)
this.$refs["ytcamera"].setZoomFactorAction(2.0, true);

// 重置为 1.0(无缩放)
this.$refs["ytcamera"].resetZoomFactorAction(true);

uniapp按步进放大 / 缩小焦距(推荐在页面维护当前倍率):

data() {
  return {
    currentZoom: 1.0
  }
},
methods: {
  // 放大:每次 +0.5,最大 5.0(可按需调整上限)
  zoomIn() {
    this.currentZoom = Math.min(this.currentZoom + 0.5, 5.0);
    this.$refs["ytcamera"].setZoomFactorAction(this.currentZoom, true);
  },
  // 缩小:每次 -0.5,最小 1.0
  zoomOut() {
    this.currentZoom = Math.max(this.currentZoom - 0.5, 1.0);
    this.$refs["ytcamera"].setZoomFactorAction(this.currentZoom, true);
  },
  // 重置
  zoomReset() {
    this.currentZoom = 1.0;
    this.$refs["ytcamera"].resetZoomFactorAction(true);
  }
}

uniapp完整示例

<template>
    <view class="content">
        <yt-camera ref="ytcamera" style="width: 750rpx;position: absolute;top: 0;bottom: 0;left: 0;"
            @capturePhotoHandle="capturePhotoHandle" @recordingHandle="recordingHandle"
            @recordingDurationUpdateHandler="recordingDurationUpdateHandler"></yt-camera>
        <view class="time_view" v-if="isRecording">
            <text class="title">{{recordingTime}}</text>
        </view>
        <view class="btn_bg">
            <view class="btn" @click="switchCameraAction">
                <image src="/static/carema_switch.png" class="img"></image>
            </view>
            <view class="btn" @click="capturePhotoAction">
                <image src="/static/camera_capture.png" class="img"></image>
            </view>
            <view class="btn" @click="toggleTorchAction">
                <image :src="light_on?'/static/light_on.png':'/static/light_off.png'" class="img"></image>
            </view>
            <view class="btn" @click="toggleRecordingAction">
                <image :src="isRecording?'/static/luzhi_on.png':'/static/luzhi_off.png'" class="img"></image>
            </view>
        </view>
        <view class="focal-length">
            <view class="focal-length-item" @click="amplification"><text>放大</text></view>
            <view class="focal-length-item" @click="reduce"><text>缩小</text></view>
            <view class="focal-length-item" @click="reset"><text>重置</text></view>
        </view>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                title: 'Hello',
                path: "",
                light_on: false,
                isRecording: false,
                recordingTime: "00:00:00",
                currentZoom: 1.0,
            }
        },
        onLoad() {

        },
        onReady() {
            //兼容iOS--uniapp端需要加延时
            setTimeout(() => {
                this.$refs["ytcamera"].startRunning()
            }, 300)
        },
        onShow() {
            if (this.$refs["ytcamera"] != null) {
                this.$refs["ytcamera"].startRunning()
            }
        },
        onHide() {
            if (this.$refs["ytcamera"] != null) {
                this.$refs["ytcamera"].stopRunning()
            }
        },
        methods: {
            //拍照回调
            capturePhotoHandle(res) {
                let path = `${res.detail.path}`;
                console.log(`拍照回调:${path}`)
            },
            //录制结束回调
            recordingHandle(res) {
                this.isRecording = false;
                let path = `${res.detail.path}`;
                console.log(`录制结束:${path}`)
            },
            //录制时间回调时间格式“00:00:10”
            recordingDurationUpdateHandler(res) {
                let durationString = res.detail.durationString ?? "00:00:00";
                this.recordingTime = `${durationString}`;
            },
            //切换摄像头
            switchCameraAction() {
                this.$refs["ytcamera"].switchCameraAction();
            },
            //开始结束录制
            toggleRecordingAction() {
                if (this.isRecording == false) {
                    this.isRecording = true;
                }
                //录制视频是否保存相册
                let saveToAlbum = true;
                //是否录制麦克风音频
                let recordAudio = true;
                this.$refs["ytcamera"].toggleRecordingAction(saveToAlbum, recordAudio)
            },
            //开关手电筒
            toggleTorchAction() {
                this.$refs["ytcamera"].toggleTorchAction();
                this.light_on = !this.light_on;
            },
            //拍照
            capturePhotoAction() {
                //拍照是否保存相册
                let saveToAlbum = true;
                this.$refs["ytcamera"].capturePhotoAction(saveToAlbum);
            },
            //放大焦距
            amplification() {
                if (this.$refs["ytcamera"] == null) return;
                this.currentZoom = Math.min(this.currentZoom + 0.5, 5.0);
                this.$refs["ytcamera"].setZoomFactorAction(this.currentZoom, true);
            },
            //缩小焦距
            reduce() {
                if (this.$refs["ytcamera"] == null) return;
                this.currentZoom = Math.max(this.currentZoom - 0.5, 1.0);
                this.$refs["ytcamera"].setZoomFactorAction(this.currentZoom, true);
            },
            //重置焦距
            reset() {
                if (this.$refs["ytcamera"] == null) return;
                this.currentZoom = 1.0;
                this.$refs["ytcamera"].resetZoomFactorAction(true);
            }

        }
    }
</script>

<style>
    .content {
        display: flex;
        flex-direction: column;
        width: 750rpx;
        flex: 1;
        background-color: antiquewhite;
        position: relative;
    }

    .logo {
        height: 200rpx;
        width: 200rpx;
        margin-top: 200rpx;
        margin-left: auto;
        margin-right: auto;
        margin-bottom: 50rpx;
    }

    .text-area {
        display: flex;
        justify-content: center;
    }

    .title {
        font-size: 36rpx;
        color: #8f8f94;
    }

    .btn_bg {
        width: 750rpx;
        display: flex;
        flex-direction: row;
        align-items: center;
        margin-top: 40rpx;
        position: absolute;
        bottom: 40;
        left: 0;
        justify-content: space-around;
    }

    .btn {
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .title {
        font-size: 32rpx;
        color: white;
    }

    .img {
        width: 70rpx;
        height: 70rpx;
    }

    .time_view {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 200rpx;
        padding: 10rpx 0;
        left: 275rpx;
        top: 40rpx;
        background-color: rgba(0, 0, 0, 0.6);
        border-radius: 10rpx;
        position: absolute;
    }

    .focal-length {
        flex-direction: row;
        display: flex;
        position: absolute;
        bottom: 200rpx;
        width: 750rpx;
        left: 0;
        align-items: center;
        justify-content: space-between;
        padding: 0 50rpx;
    }

    .focal-length-item {
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 12rpx 20rpx;
        border-radius: 5rpx;
        background-color: azure;
    }
</style>

uniapp-x 调用拍照、录制、切换摄像头、开关手电筒、设置焦距

uniapp-x开始预览,建议在onReady生命周期中调用:

onShow() {
 if (this.$refs["ytcamera"] != null) {
    (this.$refs["ytcamera"] as YtCameraElement).startRunning()
}
},
onReady() {
    (this.$refs["ytcamera"] as YtCameraElement).startRunning()
},
onHide() {
    (this.$refs["ytcamera"] as YtCameraElement).stopRunning()
},

uniapp-x调用拍照:

//拍照是否保存相册
let saveToAlbum = true;
(this.$refs["ytcamera"] as YtCameraElement).capturePhotoAction(saveToAlbum);

uniapp-x调用录制:

//拍照是否保存相册
//录制视频是否保存相册
let saveToAlbum = true;
//是否录制麦克风音频
let recordAudio = true;
(this.$refs["ytcamera"] as YtCameraElement).toggleRecordingAction(saveToAlbum, recordAudio)

uniapp-x调用切换摄像头:

(this.$refs["ytcamera"] as YtCameraElement).switchCameraAction();

uniapp-x调用开关手电筒:

(this.$refs["ytcamera"] as YtCameraElement).toggleTorchAction();

uniapp-x设置焦距(缩放倍率):

// 设置 2 倍缩放(带动画)
(this.$refs["ytcamera"] as YtCameraElement).setZoomFactorAction(2.0, true);

// 重置为 1.0(无缩放)
(this.$refs["ytcamera"] as YtCameraElement).resetZoomFactorAction(true);

uniapp-x按步进放大 / 缩小焦距(推荐在页面维护当前倍率):

data() {
  return {
    currentZoom: 1.0
  }
},
methods: {
  zoomIn() {
    this.currentZoom = Math.min(this.currentZoom + 0.5, 5.0);
    (this.$refs["ytcamera"] as YtCameraElement).setZoomFactorAction(this.currentZoom, true);
  },
  zoomOut() {
    this.currentZoom = Math.max(this.currentZoom - 0.5, 1.0);
    (this.$refs["ytcamera"] as YtCameraElement).setZoomFactorAction(this.currentZoom, true);
  },
  zoomReset() {
    this.currentZoom = 1.0;
    (this.$refs["ytcamera"] as YtCameraElement).resetZoomFactorAction(true);
  }
}

uniapp-x完整示例

<template>
    <view class="content">
        <yt-camera ref="ytcamera" style="width: 750rpx;height: 1000rpx;margin-top: 200rpx;" cameraPosition="back"
            @capturePhotoHandle="capturePhotoHandle" @recordingHandle="recordingHandle"
            @recordingDurationUpdateHandler="recordingDurationUpdateHandler"></yt-camera>
        <view class="time_view" v-show="isRecording">
            <text class="title">{{recordingTime}}</text>
        </view>
        <view class="btn_bg">
            <view class="btn" @click="switchCameraAction">
                <image src="/static/carema_switch.png" class="img"></image>
            </view>
            <view class="btn" @click="capturePhotoAction">
                <image src="/static/camera_capture.png" class="img"></image>
            </view>
            <view class="btn" @click="toggleTorchAction">
                <image :src="light_on?'/static/light_on.png':'/static/light_off.png'" class="img"></image>
            </view>
            <view class="btn" @click="toggleRecordingAction">
                <image :src="isRecording?'/static/luzhi_on.png':'/static/luzhi_off.png'" class="img"></image>
            </view>
        </view>
        <view class="focal-length">
            <view class="focal-length-item" @click="amplification"><text>放大</text></view>
            <view class="focal-length-item" @click="reduce"><text>缩小</text></view>
            <view class="focal-length-item" @click="reset"><text>重置</text></view>
        </view>
        <!-- <image :src="path"
            style="width: 750rpx;height: 300rpx; position: absolute;left: 0;bottom: 150rpx;background-color: aqua;"
            mode="aspectFill"></image> -->
    </view>
</template>

<script>
    export default {
        data() {
            return {
                path: "",
                light_on: false,
                isRecording: false,
                recordingTime: "00:00:00",
                currentZoom: 1.0,
            }
        },
        onLoad() {

        },
        onShow() {
            if (this.$refs["ytcamera"] != null) {
                (this.$refs["ytcamera"] as YtCameraElement).startRunning()
            }
        },
        onReady() {
            (this.$refs["ytcamera"] as YtCameraElement).startRunning()
        },
        onHide() {
            (this.$refs["ytcamera"] as YtCameraElement).stopRunning()
        },
        methods: {
            //拍照回调
            // #ifdef APP-IOS
            capturePhotoHandle(res) {
                console.log(res.detail)
                let status = res.detail.status ?? "1";
                let error = res.detail.msg ?? "";
                let path = res.detail.path ?? "";
                uni.showToast({
                    title: error,
                    icon: status == "0" ? 'success' : 'error'
                })
                if (path != '') {
                    this.path = `file://${path}`;
                }
            },
            recordingHandle(res) {
                console.log(res.detail)
                let status = res.detail.status ?? "1";
                let error = res.detail.msg ?? "";
                let path = res.detail.path ?? "";
                uni.showToast({
                    title: error,
                    icon: status == "0" ? 'success' : 'error'
                })
                this.isRecording = false;
            },
            //视频录制时间回调
            recordingDurationUpdateHandler(res) {
                let durationString = res.detail.durationString ?? "00:00:00";
                this.recordingTime = durationString;
            },
            // #endif
            // #ifdef APP-ANDROID
            capturePhotoHandle(res : Map<string, any>) {
                console.log(res);
                let path = `${res.get("path")}`;
                this.path = path;
            },
            recordingHandle(res : Map<string, any>) {
                this.isRecording = false;
                let path = `${res.get("path")}`;
                console.log(`录制结束:${path}`)
            },
            //视频录制时间回调
            recordingDurationUpdateHandler(res : Map<string, any>) {
                let durationString = res.get("durationString") ?? "00:00:00";
                this.recordingTime = `${durationString}`;
            },
            // #endif
            //视频录制结果回调

            //拍照
            capturePhotoAction() {
                //拍照是否保存相册
                let saveToAlbum = true;
                (this.$refs["ytcamera"] as YtCameraElement).capturePhotoAction(saveToAlbum)
            },
            //开关手电筒
            toggleTorchAction() {
                (this.$refs["ytcamera"] as YtCameraElement).toggleTorchAction()
                this.light_on = !this.light_on;
            },
            //切换摄像头
            switchCameraAction() {
                (this.$refs["ytcamera"] as YtCameraElement).switchCameraAction()
            },
            //开始结束录制
            toggleRecordingAction() {
                if (this.isRecording == false) {
                    this.isRecording = true;
                }
                //录制视频是否保存相册
                let saveToAlbum = true;
                //是否录制麦克风音频
                let recordAudio = true;
                (this.$refs["ytcamera"] as YtCameraElement).toggleRecordingAction(saveToAlbum, recordAudio)
            },
            //放大焦距
            amplification() {
                if ((this.$refs["ytcamera"] as YtCameraElement) == null) return;
                this.currentZoom = Math.min(this.currentZoom + 0.5, 5.0);
                (this.$refs["ytcamera"] as YtCameraElement).setZoomFactorAction(this.currentZoom, true);
            },
            //缩小焦距
            reduce() {
                if ((this.$refs["ytcamera"] as YtCameraElement) == null) return;
                this.currentZoom = Math.max(this.currentZoom - 0.5, 1.0);
                (this.$refs["ytcamera"] as YtCameraElement).setZoomFactorAction(this.currentZoom, true);
            },
            //重置焦距
            reset() {
                if ((this.$refs["ytcamera"] as YtCameraElement) == null) return;
                this.currentZoom = 1.0;
                (this.$refs["ytcamera"] as YtCameraElement).resetZoomFactorAction(true);
            }

        }
    }
</script>

<style>
    .content {
        display: flex;
        flex-direction: column;
        width: 750rpx;
        height: 100%;
        background-color: antiquewhite;
        position: relative;
    }

    .btn_bg {
        width: 750rpx;
        display: flex;
        flex-direction: row;
        align-items: center;
        margin-top: 40rpx;
        position: absolute;
        bottom: 40;
        left: 0;
        justify-content: space-around;
    }

    .btn {
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .title {
        font-size: 32rpx;
        color: white;
    }

    .img {
        width: 70rpx;
        height: 70rpx;
    }

    .time_view {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 200rpx;
        padding: 10rpx 0;
        left: 275rpx;
        top: 40rpx;
        background-color: rgba(0, 0, 0, 0.6);
        border-radius: 10rpx;
        position: absolute;
    }

    .focal-length {
        flex-direction: row;
        display: flex;
        position: absolute;
        bottom: 200rpx;
        width: 750rpx;
        left: 0;
        align-items: center;
        justify-content: space-between;
        padding: 0 50rpx;
    }

    .focal-length-item {
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 12rpx 20rpx;
        border-radius: 5rpx;
        background-color: azure;
    }
</style>

更多好用实惠插件

隐私、权限声明

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

android:<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> iOS:NSCameraUsageDescription、NSMicrophoneUsageDescription、NSPhotoLibraryAddUsageDescription

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

插件不采集任何数据

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