# camera

相机组件

# 兼容性

Web 微信小程序 Android iOS HarmonyOS HarmonyOS(Vapor)
x 4.41 4.61 4.61 x x

# 属性

名称 类型 默认值 兼容性 描述
flash auto | on | off | torch -
闪光灯,值为auto, on, off, torch
合法值 兼容性 描述
auto
自动
on
off
torch
常亮
device-position back | front -
前置或后置,值为front, back
合法值 兼容性 描述
back
后置
front
前置
mode normal | scanCode -
(string)
应用模式,只在初始化时有效,不能动态变更
合法值 兼容性 描述
normal
相机模式
scanCode
扫码模式
resolution low | medium | high -
(string)
分辨率,不支持动态修改
合法值 兼容性 描述
low
medium
high
frame-size small | medium | large -
(string)
指定期望的相机帧数据尺寸
合法值 兼容性 描述
small
小尺寸帧数据
medium
中尺寸帧数据
large
大尺寸帧数据
photo-resolution low | medium | high | original -
(string)
指定期望的拍照图片分辨率,不支持动态修改
合法值 兼容性 描述
low
低分辨率
medium
中分辨率
high
高分辨率
original
原始分辨率,相机的原始分辨率拍照质量最高,但是保存速度可能会较慢
@stop (event: UniCameraStopEvent) => void -
摄像头在非正常终止时触发,如退出后台等情况
@error (event: UniCameraErrorEvent) => void -
用户不允许使用摄像头时触发
@initdone (event: UniCameraInitDoneEvent) => void -
(eventhandle)
相机初始化完成时触发,e.detail = {maxZoom}
@scancode (event: UniCameraScanCodeEvent) => void -
(eventhandle)
在扫码识别成功时触发,仅在 mode="scanCode" 时生效

# 事件

# UniCameraStopEvent

# UniCameraStopEventDetail
# UniCameraStopEventDetail 的属性值
名称 类型 必填 默认值 兼容性 描述
errorCause string - - -
errSubject string - - -
errCode number - - -
errMsg string - - -
data Object - - -
cause Object - - -

# UniCameraErrorEvent

# UniCameraErrorEventDetail
# UniCameraErrorEventDetail 的属性值
名称 类型 必填 默认值 兼容性 描述
msg string - - -
errSubject string - - -
errCode number - - -
errMsg string - - -
data Object - - -
cause Object - - -

# UniCameraInitDoneEvent

# UniCameraInitDoneEventDetail
# UniCameraInitDoneEventDetail 的属性值
名称 类型 必填 默认值 兼容性 描述
maxZoom number - - -

# UniCameraScanCodeEvent

# UniCameraScanCodeEventDetail
# UniCameraScanCodeEventDetail 的属性值
名称 类型 必填 默认值 兼容性 描述
type string - - -
result string - - -
rawData string - - -
charSet string - - -
scanArea number[] - - -

# 上下文对象API

camera组件的操作api为uni.createCameraContext()

给camera组件设一个id属性,将id的值传入uni.createCameraContext(),即可得到camera组件的上下文对象,进一步可使用.takePhoto().startRecord()等方法。

# 示例

示例为hello uni-app x alpha分支,与最新HBuilderX Alpha版同步。与最新正式版同步的master分支示例另见

该 API 不支持 Web,请运行 hello uni-app x 到 App 平台体验

扫码体验(手机浏览器跳转到App直达页)
<template>
	<view style="flex: 1;">
		<camera style="width: 100%; height: 300px;" :resolution="'medium'" :device-position="devicePosition" photo-resolution="high"
			:flash="flash" :frame-size="frameSize" @stop="handleStop" @error="handleError" @initdone="handleInitDone">
		</camera>

		<scroll-view style="flex: 1;">
			<view>
        		<button type="default" @click="handleScanCode">扫码</button>
				<button type="default" @click="switchDevicePosition">切换前后摄像头</button>
				<button type="default" @click="switchFlash">闪光灯</button>

				<button type="default" @click="setOnFrameListener">设置帧数据监听</button>
				<button type="default" @click="startFrameListener">开始捕捉帧数据</button>
				<button type="default" @click="stopFrameListener">停止捕捉帧数据</button>
				<view>
					<view class="uni-title">
						<text class="uni-title-text">设置预览缩放</text>
					</view>
					<view class="uni-camera-wrapper">
						<slider class="uni-camera-test-host" :disabled="maxZoom == 0" :show-value="true" :min="1"
							:max="maxZoom" :value="1" @change="zoomSliderChange" />
					</view>
				</view>

				<view>
					<view class="uni-title">
						<text class="uni-title-text">拍摄照片示例</text>
						<button type="default" @click="handleTakePhoto">拍摄照片</button>
						<radio-group style="flex-direction: row;" name="成像质量" @change="takePhotoQualityChange">
							<radio value="normal" :checked="true">普通质量</radio>
							<radio value="low">低质量</radio>
							<radio value="high">高质量</radio>
							<radio value="original">原图</radio>
						</radio-group>
					</view>
					<view class="uni-camera-wrapper">
						<image class="uni-camera-test-host-without-flex" style="width: 150px;height: 150px;"
							v-if="imageSrc != ''" :src="imageSrc"></image>
					</view>
				</view>

				<view>
					<view class="uni-title">
						<text class="uni-title-text">录制视频示例</text>
						<view style="flex-direction: row;margin-top: 8px;">
							<text class="uni-title-size">录制时长:</text>
							<input class="uni-title-size"
								style="width: 50px; margin-left: 10px;border: 0.5px solid grey;text-align: right;"
								type="number" @input="timeoutInput" :value="timeout" />
							<text class="uni-title-size" style="margin-left: 8px;"></text>
						</view>
						<button type="default" style="font-family: monospace;margin-top: 8px;" @click="startRecord"
							:disabled="startRecordStatus">{{ startRecordStatus ? `${remain}秒` : "录制视频" }}</button>
						<button type="default" @click="stopRecord">停止录制</button>
						<radio-group style="flex-direction: row;margin-top: 8px;" name="是否压缩"
							@change="startRecordCompressChange">
							<radio value="0" :checked="true">未启动视频压缩</radio>
							<radio value="1">启动视频压缩</radio>
						</radio-group>
					</view>
					<view class="uni-camera-wrapper">
						<video class="uni-camera-test-host-without-flex" style="width: 300px;height: 300px;"
							v-if="videoSrc != ''" :src="videoSrc" :controls="true"></video>
					</view>
				</view>
			</view>
		</scroll-view>
	</view>
</template>

<script setup lang="uts">
	const devicePosition = ref("back")
	const flash = ref("off")
	const frameSize = ref("medium")
	let listener: CameraContextCameraFrameListener | null = null
	const maxZoom = ref(0)
	const imageSrc = ref("")
	let quality = "normal"
	const timeout = ref(30)
	let compressed = false
	const videoSrc = ref("")
	const startRecordStatus = ref(false)
	const remain = ref(0)
	let intervalId = -1
	let timeoutStr = '30'

	const handleScanCode = () => {
		uni.navigateTo({
			url:"/pages/component/camera/camera-scan-code"
		})
	}

	const switchDevicePosition = () => {
		devicePosition.value = devicePosition.value == "back" ? "front" : "back"
	}

	const switchFlash = () => {
		flash.value = flash.value == "torch" ? "off" : "torch"
	}

	const handleStop = (e : UniCameraStopEvent) => {
		console.log("stop", e.detail);
	}

	const handleError = (e : UniCameraErrorEvent) => {
		console.log("error", e.detail);
	}

	const handleInitDone = (e : UniCameraInitDoneEvent) => {
		console.log("initdone", e.detail);
		maxZoom.value = e.detail.maxZoom ?? 0
	}

	const zoomSliderChange = (event : UniSliderChangeEvent) => {
		const value = event.detail.value
		const context = uni.createCameraContext();
		context?.setZoom({
			zoom: value,
			success: (e : any) => {
				console.log(e);
			}
		} as CameraContextSetZoomOptions)
	}

	const handleTakePhoto = () => {
		const context = uni.createCameraContext();
		context?.takePhoto({
			quality: quality,
			selfieMirror: false,
			success: (res : CameraContextTakePhotoResult) => {
				console.log("res.tempImagePath", res.tempImagePath);
				imageSrc.value = res.tempImagePath ?? ''
			},
			fail: (e : any) => {
				console.log("take photo", e);
			}
		} as CameraContextTakePhotoOptions)
	}

	const takePhotoQualityChange = (event : UniRadioGroupChangeEvent) => {
		quality = event.detail.value
		console.log("quality", quality);
	}

	const setOnFrameListener = () => {
		const context = uni.createCameraContext();
		listener = context?.onCameraFrame((frame : CameraContextOnCameraFrame) => {
			console.log("OnFrame :", frame);
		})
	}

	const startFrameListener = () => {
		listener?.start({
			success: (res : any) => {
				console.log("startFrameListener success", res);
			}
		} as CameraContextCameraFrameListenerStartOptions)
	}

	const stopFrameListener = () => {
		listener?.stop({
			success: (res : any) => {
				console.log("stopFrameListener success", res);
			}
		} as CameraContextCameraFrameListenerStopOptions)
	}

  const getTimeout = () : number => {
  	let value = parseInt(timeoutStr)
  	if (Number.isNaN(value)) {
  		return 30
  	} else {
  		if (value < 1) {
  			return 1
  		} else if (value > 60 * 5) {
  			return 60 * 5
  		} else {
  			return value
  		}
  	}
  }

	const startRecord = () => {
		const context = uni.createCameraContext();
		let timeoutValue = getTimeout()
		timeout.value = timeoutValue
		context?.startRecord({
			timeout: timeoutValue,
			selfieMirror: false,
			timeoutCallback: (res : any) => {
				console.log("timeoutCallback", res);
				startRecordStatus.value = false
				if (typeof res != "string") {
					const result = res as CameraContextStartRecordTimeoutResult
					videoSrc.value = result.tempVideoPath ?? ''
				}
				clearInterval(intervalId)
			},
			success: (res : any) => {
				startRecordStatus.value = true
				console.log("start record success", res);
				remain.value = timeoutValue
				intervalId = setInterval(() => {
					if (remain.value <= 0) {
						clearInterval(intervalId)
					} else {
						remain.value--
					}
				}, 1000)
			},
			fail: (res : any) => {
				console.log("start record fail", res);
				startRecordStatus.value = false
				remain.value = 0
				clearInterval(intervalId)
			}
		} as CameraContextStartRecordOptions)
	}

	const stopRecord = () => {
		startRecordStatus.value = false
		const context = uni.createCameraContext();
		context?.stopRecord({
			compressed: compressed,
			success: (res : CameraContextStopRecordResult) => {
				console.log("stop record success", res);
				videoSrc.value = res.tempVideoPath ?? ''
			},
			fail: (res : any) => {
				console.log("stop record fail", res);
			}
		} as CameraContextStopRecordOptions)
		clearInterval(intervalId)
		remain.value = 0
	}

	const startRecordCompressChange = (event : UniRadioGroupChangeEvent) => {
		compressed = parseInt(event.detail.value) == 1
	}

	const timeoutInput = (event : UniInputEvent) => {
		timeoutStr = event.detail.value
	}

</script>

<style>
	.uni-title {
		padding: 10px 0;
	}

	.uni-title-text {
		font-size: 15px;
		font-weight: bold;
	}

	.uni-camera-wrapper {
		display: flex;
		padding: 8px 13px;
		margin: 5px 0;
		flex-direction: row;
		flex-wrap: nowrap;
		background-color: #ffffff;
	}

	.uni-camera-test-host {
		height: 28px;
		padding: 0px;
		flex: 1;
		background-color: #ffffff;
	}

	.uni-camera-test-host-without-flex {
		height: 28px;
		padding: 0px;
		background-color: #ffffff;
	}

	.uni-title-size {
		font-size: 22px;
	}
</style>

# 依赖库版本

Android端实现相机组件所使用的依赖库

"androidx.camera:camera-core:1.4.1",
"androidx.camera:camera-camera2:1.4.1",
"androidx.camera:camera-lifecycle:1.4.1",
"androidx.camera:camera-view:1.4.1",
"androidx.appcompat:appcompat:1.7.0"

# 关于相机组件扫码能力的注意事项

  • camera组件仅在 uni-app x 项目中支持,扫码功能需更新到 4.71 及以上版本。

  • 扫码功能是独立模块,目前需要手动配置。后续版本会提供可视化界面配置。

    在manfiest.json中的 "app-android" -> "distribute" -> "modules" 节点下手动添加 "uni-barcode-scanning",如下示例:

"app-android" : {
    "distribute" : {
        "modules" : {
            "uni-barcode-scanning" : {}
        }
    }
}

# 参见