# camera

相机组件

# 兼容性

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

# 属性

名称 类型 默认值 兼容性 描述
flash string -
闪光灯,值为auto, on, off
合法值 兼容性 描述
auto
自动
on
off
torch
device-position string -
前置或后置,值为front, back
合法值 兼容性 描述
back
后置
front
前置
mode string -
(string)
应用模式,只在初始化时有效,不能动态变更
合法值 兼容性 描述
normal
scanCode
resolution string -
(string)
分辨率,不支持动态修改
合法值 兼容性 描述
low
medium
high
frame-size string -
(string)
指定期望的相机帧数据尺寸
合法值 兼容性 描述
small
medium
large
@stop (event: UniCameraStopEvent) => void -
摄像头在非正常终止时触发,如退出后台等情况
@error (event: UniCameraErrorEvent) => void -
用户不允许使用摄像头时触发
@initdone (event: UniCameraInitDoneEvent) => void -
-
@scancode eventhandle -
-

# 事件

# UniCameraStopEvent

# UniCameraStopEventDetail
# UniCameraStopEventDetail 的属性值
名称 类型 必填 默认值 兼容性 描述
errorCause string - -

# UniCameraErrorEvent

# UniCameraErrorEventDetail
# UniCameraErrorEventDetail 的属性值
名称 类型 必填 默认值 兼容性 描述
msg string - -

# UniCameraInitDoneEvent

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

# 上下文对象API

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

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

# 示例

hello uni-app x

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

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

		<scroll-view style="flex: 1;">
			<view>
				<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>
	export default {
		data() {
			return {
				devicePosition: "back",
				flash: "off",
				frameSize: "medium",
				listener: null as CameraContextCameraFrameListener | null,
				maxZoom: 0,
				imageSrc: "",
				quality: "normal",
				timeout: 30,
				compressed: false,
				videoSrc: "",
				startRecordStatus: false,
				remain: 0,
				intervalId: -1,
				timeoutStr: '30'
			}
		},
		onLoad() {

		},

		methods: {
			switchDevicePosition() {
				this.devicePosition = this.devicePosition == "back" ? "front" : "back"
			},

			switchFlash() {
				this.flash = this.flash == "torch" ? "off" : "torch"
			},

			handleStop(e : UniCameraStopEvent) {
				console.log("stop", e.detail);
			},
			handleError(e : UniCameraErrorEvent) {
				console.log("error", e.detail);
			},
			handleInitDone(e : UniCameraInitDoneEvent) {
				console.log("initdone", e.detail);
				this.maxZoom = e.detail.maxZoom ?? 0
			},
			zoomSliderChange(event : UniSliderChangeEvent) {
				const value = event.detail.value
				const context = uni.createCameraContext();
				context?.setZoom({
					zoom: value,
					success: (e : any) => {
						console.log(e);
					}
				} as CameraContextSetZoomOptions)
			},
			handleTakePhoto() {
				const context = uni.createCameraContext();
				context?.takePhoto({
					quality: this.quality,
					selfieMirror: false,
					success: (res : CameraContextTakePhotoResult) => {
						console.log("res.tempImagePath", res.tempImagePath);
						this.imageSrc = res.tempImagePath ?? ''
					},
					fail: (e : any) => {
						console.log("take photo", e);
					}
				} as CameraContextTakePhotoOptions)
			},
			takePhotoQualityChange(event : UniRadioGroupChangeEvent) {
				this.quality = event.detail.value
				console.log("quality", this.quality);
			},

			setOnFrameListener() {
				const context = uni.createCameraContext();
				this.listener = context?.onCameraFrame((frame : CameraContextOnCameraFrame) => {
					console.log("OnFrame :", frame);
				})
			},
			startFrameListener() {
				this.listener?.start({
					success: (res : any) => {
						console.log("startFrameListener success", res);
					}
				} as CameraContextCameraFrameListenerStartOptions)

			},
			stopFrameListener() {
				this.listener?.stop({
					success: (res : any) => {
						console.log("stopFrameListener success", res);
					}
				} as CameraContextCameraFrameListenerStopOptions)
			},
			startRecord() {
				const context = uni.createCameraContext();
				let timeout = this.getTimeout()
				this.timeout = timeout
				context?.startRecord({
					timeout: timeout,
					selfieMirror: false,
					timeoutCallback: (res : any) => {
						console.log("timeoutCallback", res);
						this.startRecordStatus = false
						if (typeof res != "string") {
							const result = res as CameraContextStartRecordTimeoutResult
							this.videoSrc = result.tempVideoPath ?? ''
						}
						clearInterval(this.intervalId)
					},
					success: (res : any) => {
						this.startRecordStatus = true
						console.log("start record success", res);
						this.remain = timeout
						this.intervalId = setInterval(() => {
							if (this.remain <= 0) {
								clearInterval(this.intervalId)
							} else {
								this.remain--
							}
						}, 1000)
					},
					fail: (res : any) => {
						console.log("start record fail", res);
						this.startRecordStatus = false
						this.remain = 0
						clearInterval(this.intervalId)
					}
				} as CameraContextStartRecordOptions)
			},
			stopRecord() {
				this.startRecordStatus = false
				const context = uni.createCameraContext();
				context?.stopRecord({
					compressed: this.compressed,
					success: (res : CameraContextStopRecordResult) => {
						console.log("stop record success", res);
						this.videoSrc = res.tempVideoPath ?? ''
					},
					fail: (res : any) => {
						console.log("stop record fail", res);
					}
				} as CameraContextStopRecordOptions)
        clearInterval(this.intervalId)
        this.remain = 0
			},
			startRecordCompressChange(event : UniRadioGroupChangeEvent) {
				this.compressed = parseInt(event.detail.value) == 1
			},
			timeoutInput(event : UniInputEvent) {
				this.timeoutStr = event.detail.value
			},
			getTimeout() : number {
				let value = parseInt(this.timeoutStr)
				// #ifdef APP-ANDROID
				if (value == NaN) {
				// #endif
				// #ifndef APP-ANDROID
				if (value !== value) {
				// #endif
					return 30
				} else {
					if (value < 1) {
						return 1
					} else if (value > 60 * 5) {
						return 60 * 5
					} else {
						return 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>

# 参见