更新记录

1.0.0(2026-09-26)

原生内嵌相机


平台兼容性

uni-app(5.26)

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

yao-embed-camera

原生内嵌相机 UTS 组件(uni-app 兼容模式组件),在页面中内嵌一个相机预览区域,支持拍照、手电筒、前后摄像头切换。

开发文档

UTS 语法 UTS uni-app兼容模式组件

必须使用 nvue 页面

本组件是原生内嵌组件,App 端必须在 .nvue 页面中使用(.vue 页面无法渲染原生预览)。

使用示例(Vue2 · nvue 页面)

<template>
    <view class="page">

        <view class="camera-box" :style="{ width: camW + 'px', height: camH + 'px' }">
            <yao-embed-camera ref="cameraRef"
                :style="{ width: camW + 'px', height: camH + 'px' }"
                @photo-taken="onPhotoTaken"
                @picture-size="onPictureSize"
                @camera-error="onCameraError"
                @camera-switched="onCameraSwitched" />
        </view>

        <view class="btn-row">
            <view class="btn" @click="onShoot"><text class="btn-txt">拍照</text></view>
            <view class="btn" @click="onSwitch"><text class="btn-txt">切换</text></view>
            <view class="btn" @click="onFlash"><text class="btn-txt">闪光灯</text></view>
        </view>

        <image v-if="photoPath" class="photo" :src="photoPath" mode="aspectFit"></image>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                camW: 375,
                camH: 500,// 默认 3:4
                photoPath: ''
            }
        },
        onLoad() {
            const sys = uni.getSystemInfoSync();
            this.camW = sys.windowWidth;
            this.camH = Math.round(this.camW * 4 / 3);
        },
        methods: {

            getParam(e, key) {
                const d = (e && e.detail) ? e.detail : {};
                if (d[key] != null) return d[key];
                if (d.dynamicJSONFields && d.dynamicJSONFields[key] != null) {
                    return d.dynamicJSONFields[key];
                }
                return null;
            },
            onPictureSize(e) {
                const w = this.getParam(e, 'width');
                const h = this.getParam(e, 'height');
                if (w && h) {
                    // 按实际照片比例适配预览框,保证所见即所得
                    this.camH = Math.round(this.camW * h / w);
                }
            },
            onPhotoTaken(e) {
                const path = this.getParam(e, 'path');
                if (path) this.photoPath = path;   // 照片在应用缓存目录,可直接显示
            },
            onCameraError(e) {
                uni.showToast({ title: this.getParam(e, 'message') || '相机错误', icon: 'none' });
            },
            onCameraSwitched(e) {
                const facing = this.getParam(e, 'facing');
                uni.showToast({ title: facing === 'front' ? '已切到前置' : '已切到后置', icon: 'none' });
            },
            onShoot() {
                this.$refs.cameraRef.takePhoto();
            },
            onSwitch() {
                this.$refs.cameraRef.switchCamera();
            },
            onFlash() {
                this.$refs.cameraRef.toggleFlashlight();
            }
        }
    }
</script>

<style>
    .page { flex: 1; background-color: #000000; align-items: center; }
    .camera-box { background-color: #222222; }
    .btn-row { flex-direction: row; margin-top: 20rpx; }
    .btn { padding: 16rpx 40rpx; margin: 0 16rpx; background-color: #007aff; border-radius: 8rpx; }
    .btn-txt { color: #ffffff; font-size: 28rpx; }
    .photo { width: 375px; height: 500px; margin-top: 20rpx; }
</style>

使用示例(Vue3 · nvue 页面)

<template>
    <view class="page">
        <view class="camera-box" :style="{ width: camW + 'px', height: camH + 'px' }">
            <yao-embed-camera ref="cameraRef"
                :style="{ width: camW + 'px', height: camH + 'px' }"
                @photo-taken="onPhotoTaken"
                @picture-size="onPictureSize"
                @camera-error="onCameraError"
                @camera-switched="onCameraSwitched" />
        </view>

        <view class="btn-row">
            <view class="btn" @click="onShoot"><text class="btn-txt">拍照</text></view>
            <view class="btn" @click="onSwitch"><text class="btn-txt">切换</text></view>
        </view>

        <image v-if="photoPath" class="photo" :src="photoPath" mode="aspectFit"></image>
    </view>
</template>

<script setup>
    import { ref } from 'vue'
    import { onLoad } from '@dcloudio/uni-app'

    const cameraRef = ref(null)
    const camW = ref(375)
    const camH = ref(500)
    const photoPath = ref('')

    onLoad(() => {
        const sys = uni.getSystemInfoSync();
        camW.value = sys.windowWidth;
        camH.value = Math.round(camW.value * 4 / 3);
    })

    const getParam = (e, key) => {
        const d = (e && e.detail) ? e.detail : {};
        if (d[key] != null) return d[key];
        if (d.dynamicJSONFields && d.dynamicJSONFields[key] != null) {
            return d.dynamicJSONFields[key];
        }
        return null;
    }

    const onPictureSize = (e) => {
        const w = getParam(e, 'width');
        const h = getParam(e, 'height');
        if (w && h) {
            camH.value = Math.round(camW.value * h / w);
        }
    }

    const onPhotoTaken = (e) => {
        const path = getParam(e, 'path');
        if (path) photoPath.value = path;
    }

    const onCameraError = (e) => {
        uni.showToast({ title: getParam(e, 'message') || '相机错误', icon: 'none' });
    }

    const onCameraSwitched = (e) => {
        const facing = getParam(e, 'facing');
        uni.showToast({ title: facing === 'front' ? '已切到前置' : '已切到后置', icon: 'none' });
    }

    const onShoot = () => cameraRef.value.takePhoto()
    const onSwitch = () => cameraRef.value.switchCamera()
</script>

<style>
    .page { flex: 1; background-color: #000000; align-items: center; }
    .camera-box { background-color: #222222; }
    .btn-row { flex-direction: row; margin-top: 20rpx; }
    .btn { padding: 16rpx 40rpx; margin: 0 16rpx; background-color: #007aff; border-radius: 8rpx; }
    .btn-txt { color: #ffffff; font-size: 28rpx; }
    .photo { width: 375px; height: 500px; margin-top: 20rpx; }
</style>

API

方法(通过 ref 调用)

方法 说明
takePhoto() 拍照,完成后触发 photo-taken 事件
toggleFlashlight() 切换手电筒开关(仅后置摄像头有效;切换摄像头后状态自动重置为关闭)
switchCamera() 切换前/后摄像头,成功后触发 camera-switched 事件

事件

事件 回调参数 说明
photo-taken { path: string } 拍照完成,path 为照片本地路径(应用缓存目录,可直接作为 image src)
picture-size { width: number, height: number } 照片尺寸(竖屏宽高,建议据此适配预览框比例)
camera-error { message: string } 权限被拒绝 / 相机打开失败等错误信息
camera-switched { facing: string } 摄像头已切换,"front" 前置 / "back" 后置

平台配置

HarmonyOS

组件初始化前会自动弹出相机授权框,但需先在项目根目录 harmony-configs/entry/src/main/module.json5 中声明权限(若无 harmony-configs 目录请先运行一次鸿蒙构建让其生成,或手动创建):

{
  "module": {
    "requestPermissions": [
      { "name": "ohos.permission.INTERNET" },
      {
        "name": "ohos.permission.CAMERA",
        "reason": "$string:camera_permission_reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "inuse"
        }
      }
    ]
  }
}

并在 harmony-configs/entry/src/main/resources/base/element/string.json(zh_CN / en_US 同理)中补充文案:

{
  "string": [
    // ...原有条目保持不变...
    {
      "name": "camera_permission_reason",
      "value": "需要使用相机进行拍照"
    }
  ]
}

隐私、权限声明

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

摄像头权限

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

插件不采集任何数据

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

无

暂无用户评论。