# 原生自定义相机拍照、视频录制 （ima-camera-view）

`原生自定义相机拍照、视频录制 （ima-camera-view）`是基于原生相机开发的UTS插件，支持`相机拍照`、`视频录制`
、可实现`点击聚焦`、`手势缩放`、
`自定义布局`、`自定义蒙版`（用于人脸拍照，身份证拍照等）。

## ⚠️注意️
由于之前技术选型不支持继续扩展相机的新特性，`本插件（ima-camera-view）继续维护，只修复现存的一些bug，不再新增新功能`，
建议使用ima-camerax-view这个相机组件插件，[ima-camerax-view 地址](https://ext.dcloud.net.cn/plugin?name=ima-camerax-view)，
如果不追求如：**超广角镜头切换（0.5x，`changeWideAngle`）**、**按倍率缩放（`setCameraZoom`，如 0.5 / 1.0 / 2.0）**、**多摄逻辑镜头切换**、
**Camera2 白平衡 / HDR / 曝光补偿**、**录像进度（`onVideoTakenProgress`）与设备方向变化（`onOrientationChanged`）回调**等新特性的话，扔建议继续使用本插件。

- `ima-camerax-view【新插件】`：[插件地址](https://ext.dcloud.net.cn/plugin?name=ima-camerax-view)
- `ima-camera-view【本插件】`：[插件地址](https://ext.dcloud.net.cn/plugin?name=ima-camera-view)
- `ima-camerax-view【新插件】`与 `ima-camera-view【本插件】`组件 API 保持基本一致，便于从 CameraView 版平滑迁移

## 支持功能

- 打开、关闭摄像头预览
- 拍照、快照拍照
- 录制视频、快照录制视频
- 设置摄像头缩放级别
- 设置相机白平衡
- 设置相机HDR
- 设置相机曝光
- 设置摄像头方向
- 设置闪光灯模式
- 设置相机使用设备方向
- 设置相机网格及颜色
- 设置音频（录制视频时）
- 设置圆角、圆预览（可自定义）
- 外接摄像头预览方向修正（`previewRotation`，Android）
- 设置拍照、录制视频的声音（可自定义）
- 蓝牙自拍杆（可自定义）
- 手机快捷键（可自定义）

## 自定义调整

- 自定义调整页面地址：`uni_modules/ima-camera-view/utssdk/app-android/index.vue`
- 蓝牙自拍杆、手机快捷键的自定义，可以参考文件中的`shortcutListener`方法
- 设置拍照、录制视频的声音，可以参考文件中的`photoSound`、`videoSound`方法

## 需要权限

- 摄像头、音频、文件读取、文件写入、震动

```text
"android.permission.CAMERA",
"android.permission.RECORD_AUDIO",
"android.permission.VIBRATE"
"android.permission.READ_EXTERNAL_STORAGE"
"android.permission.WRITE_EXTERNAL_STORAGE"
```

- 即：在`manifest.json`中的`distribute.android.permissions`加入

```text
// 拍摄照片和视频时需要
<uses-permission android:name="android.permission.CAMERA" /> 
// 拍摄视频时需要Audio.ON（默认）
<uses-permission android:name="android.permission.RECORD_AUDIO" /> 
// 读取拍照、录像文件文件时需要
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
// 报错拍照、录像文件文件时需要（默认保存到沙盒缓存）
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 
// 震动权限
<uses-permission android:name="android.permission.VIBRATE" />
```

## 快门声音素材

- 将需要的快门声音放在`uni_modules/ima-camera-view/utssdk/app-android/assets`下即可
- [熊猫办公](https://www.tukuppt.com/yinxiaomuban/kuaimenshengyin.html)
- [站长素材](https://sc.chinaz.com/tag_yinxiao/kuaimen.html)

## 使用示例【此示例的代码只实现了`相机拍照`的逻辑，更多示例请导入项目】

- 新建一个`camera.nvue`的文件
- ⚠️注意️：只能在`.nvue`、`.uvue`的文件后缀下才生效，不支持`.vue`

```nvue
<template>
	<view class="ima-camera" :style="{ width: windowWidth, height: windowHeight }">
		<ima-camera-view
			ref="cameraRef"
			class="camera-view"
			:style="{ width: windowWidth + 'px', height: windowHeight + 'px' }"
			flash="on"
			@onPictureTaken="onPictureTaken"
			onFocusStart="onFocusStart"
		/>
		<view class="camera-menu">
			<!--返回键-->
			<cover-image @tap="back" class="camera-menu-button back" src="/static/camera/back.png" />
			<!--快门键-->
			<cover-image
				@tap="takePhoto"
				class="camera-menu-button shutter"
				src="/static/camera/shutter.png"
			/>
			<!--反转键-->
			<cover-image @tap="flip" class="camera-menu-button flip" src="/static/camera/flip.png" />
		</view>
	</view>
</template>

<script>
let _this = null
export default {
	data() {
		return {
			windowWidth: '', //屏幕可用宽度
			windowHeight: '', //屏幕可用高度
			facing: 'back'
		}
	},
	onLoad() {
		_this = this
		this.initCamera()
	},
	methods: {
		//初始化相机
		initCamera() {
			console.log('初始化相机')
			uni.getSystemInfo({
				success: (res) => {
					_this.windowWidth = res.windowWidth
					_this.windowHeight = res.windowHeight
				}
			})
		},
		onFocusStart(e) {
			console.log('聚焦', e)
		},
		takePhoto() {
			console.log('拍照', this.facing)
			this.$refs.cameraRef.takePhoto()
		},
		//返回
		back() {
			console.log('返回上一页', this.facing)
			uni.navigateBack()
		},
		//反转
		flip() {
			console.log('镜头反转', this.facing)
			this.facing = this.facing === 'back' ? 'front' : 'back'
			this.$refs.cameraRef.changeFacing(this.facing)
		},
		onPictureTaken(e) {
			console.log('拍照结果', e.detail)
			_this.snapshotsrc = e.detail?.path || ''
			_this.getTakenRes()
			uni.navigateBack()
		},
		//设置
		getTakenRes() {
			console.log('返回结果给上一页')
			let pages = getCurrentPages()
			let prevPage = pages[pages.length - 2] //上一个页面
			//直接调用上一个页面的setImage()方法，把数据存到上一个页面中去
			prevPage.$vm.setImage({ path: _this.snapshotsrc })
		}
	}
}
</script>

<style lang="scss">
.ima-camera {
	justify-content: center;
	align-items: center;

	.camera-view {
		width: 100%;
		background: #111;
	}

	.camera-menu {
		position: absolute;
		left: 0;
		bottom: 0;
		width: 750rpx;
		height: 180rpx;
		z-index: 98;
		align-items: center;
		justify-content: center;

		&-button {
			width: 80rpx;
			height: 80rpx;
			position: absolute;
			bottom: 50rpx;
			z-index: 99;
			align-items: center;
			justify-content: center;
		}

		.back {
			left: 30rpx;
		}

		.shutter {
			width: 130rpx;
			height: 130rpx;
			left: 310rpx;
			bottom: 25rpx;
		}

		.flip {
			right: 30rpx;
		}
	}
}
</style>
```

## 外接摄像头方向修正（Android）

适用于**无内置摄像头**或外接 USB 摄像头的设备（如横屏平板、工控一体机）。当物理安装的摄像头与屏幕方向不一致，导致预览画面旋转 90° 等情况时，可通过 `previewRotation` 固定修正预览与拍照方向。

### 使用方式

**方式一：属性（推荐）**

```nvue
<ima-camera-view
  ref="cameraRef"
  :previewRotation="90"
  :previewCornerRadiusRate="0.5"
  facing="front"
/>
```

**方式二：方法动态调整**

```javascript
this.$refs.cameraRef.changePreviewRotation(90)   // 顺时针 90°
this.$refs.cameraRef.changePreviewRotation(-90)  // 逆时针 90°（等价 270°）
```

### 参数说明

| 取值 | 说明 |
|------|------|
| `0` | 不修正（默认） |
| `90` / `-270` | 顺时针旋转 90° |
| `180` / `-180` | 旋转 180° |
| `270` / `-90` | 顺时针旋转 270°（逆时针 90°） |

### 注意事项

- 仅 **`android`** 端生效；与 `orientation`（设备方向跟随）不同，`previewRotation` 用于**固定补偿外接摄像头安装角度**，不随手机旋转而改变。
- 启用非 `0` 的 `previewRotation` 后，组件会自动使用 `TextureView` 预览；拍照结果会同步叠加该角度修正。
- 若预览方向仍不对，请依次尝试 `90`、`-90`、`180`，以实际安装方向为准。
- 录像方向暂未做同等叠加修正；若录像也有方向问题，可在业务层二次处理或反馈 issue。

## 常见的比例的定义（widthRatio，heightRatio）

```typescript
// 正方形
AspectRatio.of(1, 1)      // 1:1

// 竖屏比例
AspectRatio.of(9, 16)     // 9:16 (手机竖屏)
AspectRatio.of(3, 4)      // 3:4
AspectRatio.of(2, 3)      // 2:3
AspectRatio.of(10, 16)    // 10:16 (5:8)

// 横屏比例
AspectRatio.of(16, 9)     // 16:9 (宽屏)
AspectRatio.of(4, 3)      // 4:3 (传统)
AspectRatio.of(3, 2)      // 3:2 (照片)
AspectRatio.of(16, 10)    // 16:10 (8:5)
AspectRatio.of(21, 9)     // 21:9 (超宽屏)

// 建议比例
const AspectRatios = {
  // 1:1 正方形
  SQUARE: AspectRatio.of(1, 1),
  // 9:16 竖屏（手机默认）
  PORTRAIT: AspectRatio.of(9, 16),
  // 16:9 横屏
  LANDSCAPE: AspectRatio.of(16, 9),
  // 3:4 传统照片比例
  THREE_FOUR: AspectRatio.of(3, 4),
  // 4:3 传统相机比例
  FOUR_THREE: AspectRatio.of(4, 3)
}
```

## 不同场景的推荐值（tolerance）

```typescript
const TOLERANCE = {
  STRICT: 0.01.toFloat(),    // 非常严格，几乎精确匹配
  STANDARD: 0.05.toFloat(),  // 标准，推荐使用
  FLEXIBLE: 0.1.toFloat(),   // 灵活，兼容更多设备
  LOOSE: 0.2.toFloat()       // 宽松，可能匹配到意外比例
}
```

## Api

| 属性						| 类型							| 默认值				| 说明																																																																																															| 平台					|
|---------------|-----------------|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|
| widthRatio		| Number					| 0						| 照片尺寸比率（宽度）: 默认全屏（若widthRatio为9，heightRatio为16，则为9:16、widthRatio为3，heightRatio为4，则为3:4...）不建议值过大，常用的比例有1:1、3:4、4:3、9:16...																					| `android`		|
| heightRatio		| Number					| 0						| 照片尺寸比率（高度）: 默认全屏（若widthRatio为9，heightRatio为16，则为9:16、widthRatio为3，heightRatio为4，则为3:4...）不建议值过大，常用的比例有1:1、3:4、4:3、9:16...																					| `android`		|
| tolerance			| Number					| 0.1					| 照片尺寸容差值: 建议设为 0.05~0.15，以便稍微兼容不同设备相机实际比例差异	,值为:0～1																																																												| `android`		|
| whiteBalance	| iWhiteBalance		| "auto"			| 白平衡模式: auto（自动）、incandescent（白炽）、fluorescent（荧光）、daylight（日光）、cloudy（多云）	、loudy（多云【慎用：`兼容1.1.3版本前单词拼错的问题，将在1.2.0大版本更新后删除，建议1.1.3后的版本使用cloudy`】）	| `android`		|
| hdr						| iHdr						| "off"				| HDR模式: off（关闭）、on（开启）																																																																																		| `android`		|
| facing				| iFacing					| "back"			| 后置、前置摄像头:  back(后置摄像头)、front(前置摄像头)																																																																									| `android`		|
| flash					| iFlash					| "off"				| 闪光灯: off(关闭)、on(开启)、auto(自动)、torch(常开)																																																																									| `android`		|
| audio					| iAudio					| "on"				| 音频: on(开启)、off(关闭)、mono(单声道)、stereo(立体声)																																																																							| `android`		|
| orientation		| iOrientation		| "auto"			| 方向: auto(自动)、portrait(竖屏)、landscape(横屏)	【注意：`目前竖屏、横屏拍出来都为竖屏方式，这两个参数的使用效果一致`，为预留参数，为后期做准备】																																	| `android`		|
| grid					| iGrid						| "off"				| 网格: off(关闭)、draw_3X3(3x3)、draw_4x4(4x4)、draw_phi(phi)																																																																											| `android`		|
| gridColor			| String					| "#808080"		| 颜色值: 只支持如：#fff、#ffffff、#......等类型的颜色值																																																																								| `android`		|
| photoSuffix		| iPhotoSuffix		| "jpeg"			| 照片格式: jpeg、jpg																																																																																								| `android`		|
| mode					| String					| 'picture'		| 相机模式: picture(拍照)、video(录视频)																																																																															| `android`		|
| previewCornerRadius	| Number					| 0					| 预览圆角半径（px）；与 `previewCornerRadiusRate` 二选一或组合使用																																																																						| `android`		|
| previewCornerRadiusRate	| Number				| 0					| 预览圆角比例（相对短边）；`0.5` 且预览区域为正方形时为圆形预览																																																																					| `android`		|
| previewRotation		| Number					| 0					| 外接摄像头预览/拍照方向修正角度：`0`、`90`、`180`、`270`（支持负值如 `-90`）；详见上文「外接摄像头方向修正」																																																										| `android`		|
| gallery					| Boolean					| false				| 是否将拍照、录像文件保存到系统相册（可见媒体库）																																																																											| `android`		|
| shutter				| Boolean					| true				| 是否打开拍照声音: true(开启，此时配置`sound`才起作用)、false(关闭)																																																																			| `android`		|
| sound					| String					| ''					| 相机拍照声音文件: 将mp3音频文件放在`uni_modules/ima-camera-view/utssdk/app-android/assets`下即可，为音频文件的名称，如`xxx.mp3`,默认手机原声																																| `android`		|
| recorder			| Boolean					| true				| 是否打开录像声音: true(开启，此时配置`sound2`才起作用)、false(关闭)																																																																			| `android`		|
| sound2				| String					| ''					| 相机录像声音文件: 将mp3音频文件放在`uni_modules/ima-camera-view/utssdk/app-android/assets`下即可，为音频文件的名称，如`xxx.mp3`,默认手机原声																																| `android`		|
| vibrate				| Boolean					| false				| 是否打开拍照震动: true(开启，此时配置`duration`才起作用)、false(关闭)																																																																		| `android`		|
| duration			| Number					| 300					| 是否打开拍照震动时长，单位：毫秒（ms）																																																																																| `android`		|
| shortcut			| Boolean					| false				| 是否开启蓝牙自拍杆、手机快捷键拍照: false(关闭)、true(开启)																																																																							| `android`		|

## 方法

### 共同 方法/*  */

| 方法名称																								| 说明												| 方法参数																																																								| 平台					|
|-------------------------------------------------------|---------------------------|-----------------------------------------------------------------------------------------------------------------------|-------------|
| open																									| 打开摄像头预览								| 无																																																											| `android`		|
| close																									| 关闭摄像头预览								| 无																																																											| `android`		|
| takePhoto																							| 拍照（标准拍照流程）					| 无																																																											| `android`		|
| takeVideo(duration)																		| 开始录制视频								| duration:拍摄时长，单位：毫秒（ms）【0 表示不限制】																																					| `android`		|
| stopVideo																							| 停止视频录制								| 无																																																											| `android`		|
| changeZoom(zoom)																			| 设置摄像头缩放级别						| zoom:缩放倍数（浮点数）																																																	| `android`		|
| changeExposure(exposure)															| 设置曝光值									| exposure:曝光值（浮点数）【值为：-2～2，默认为0】																																						| `android`		|
| changeWhiteBalance(whiteBalance)											| 设置相机白平衡								| whiteBalance:参考`api`中的`whiteBalance`参数																																						| `android`		|
| changeHdr(hdr)																				| 设置相机HDR									| hdr:参考`api`中的`hdr`参数																																															| `android`		|
| changeFacing((facing))																| 设置摄像头方向								| facing:参考`api`中的`facing`参数																																												| `android`		|
| changeFlash(flash)																		| 设置闪光灯模式								| flash:参考`api`中的`flash`s参数																																													| `android`		|
| changeOrientation(orientation)												| 设置相机使用设备方向					| orientation:参考`api`中的`orientation`s参数																																							| `android`		|
| changeGrid(grid,color)																| 设置相机网格及颜色						| grid:参考`api`中的`grid`参数, color: 参考`api`中的`gridColor`参数																													| `android`		|
| changeAudio(audio)																		| 设置音频										| audio:参考`api`中的`audio`参数																																													| `android`		|
| changeSuffix(suffix)																	| 设置照片输出格式							| suffix:参考`api`中的`photoSuffix`参数																																										| `android`		|
| changeSizeSelectors(width,height,tolerance)					| 设置相机特定比例/分辨率					| width、height、tolerance 参考 `api` 中同名属性；`0` 表示使用屏幕宽高																																																| `android`		|
| changePreviewCorner(radius,radiusRate)						| 设置预览圆角								| radius: 圆角 px；radiusRate: 圆角比例（正方形下 `0.5` 为圆形）																																																		| `android`		|
| changePreviewRotation(degrees)								| 设置外接摄像头预览/拍照方向修正		| degrees: `0` / `90` / `180` / `270`（支持负值）；仅 Android																																																			| `android`		|
| changeGallery(gallery)										| 是否保存到系统相册						| gallery: `true` / `false`																																																												| `android`		|
| changeMode(mode)												| 设置相机模式								| mode: `picture` / `video`；也可直接使用 `:mode` prop																																																					| `android`		|
| takePhotoSnapshot																			| 快照拍照（适用于快速拍照场景）	| 无																																																											| `android`		|
| takeVideoSnapshot(duration)														| 快照方式录制视频							| duration:拍摄时长，单位：毫秒（ms）【0 表示不限制】																																					| `android`		|

## 事件

| 事件名称							| 说明						| 回调参数														| 平台					|
|---------------------|---------------|-----------------------------------|-------------|
| onPictureTaken			| 拍照返回数据		| ({path,width,height}: any) => {}	| `android`		|
| onVideoTakenStart		| 录制视频开始事件	| () => {}													| `android`		|
| onVideoTakenEnd			| 录制视频结束事件	| ({path,size}: any) => {}					| `android`		|
| onFocusStart				| 自动对焦开始		| ({x,y}: any) => {}								| `android`		|
| onFocusEnd					| 自动对焦结束		| ({x,y,focus}: any) => {}					| `android`		|
| onOrientationChange		| 设备方向变化		| ({angle,orientation,isPortrait,isLandscape}: any) => {}	| `android`		|
| onCameraOpened				| 相机已打开			| (data: any) => {}								| `android`		|
| onCameraClosed				| 相机已关闭			| (data: any) => {}								| `android`		|
| onCameraError					| 相机错误				| ({errorCode,reason,message}: any) => {}	| `android`		|

