feat:单拍/连拍按钮改名,正方形预览+拍照,按钮放大,完成与翻转调换

This commit is contained in:
ShenQiLun 2026-06-25 17:23:12 +08:00
parent 085855e39e
commit 834380ce5e
23 changed files with 2394 additions and 86 deletions

View File

@ -1,14 +1,18 @@
<template>
<view class="cc-page">
<!-- 相机预览 -->
<!-- 正方形相机预览 -->
<view class="cc-camera-box" :style="'width:' + camSize + 'px;height:' + camSize + 'px;'">
<ima-camera-view
ref="cameraRef"
:style="'width:' + winWidth + 'px;height:' + winHeight + 'px;'"
style="width:100%;height:100%;"
flash="off"
facing="back"
:widthRatio="1"
:heightRatio="1"
@onPictureTaken="onPictureTaken"
@onCameraOpened="onCameraOpened"
></ima-camera-view>
</view>
<!-- ═══ 顶部栏 ═══ -->
<view class="cc-topbar">
@ -21,9 +25,11 @@
<text class="cc-topbar-title">{{ capturedList.length > 0 ? '已拍 ' + capturedList.length + '/9' : '拍照' }}</text>
</view>
<view class="cc-topbar-right">
<text class="cc-topbar-confirm" @click="confirmCapture" v-if="capturedList.length > 0">
完成
</text>
<!-- 翻转按钮移到了右上角 -->
<view class="cc-flip-btn" @click="flipCamera">
<text class="cc-flip-icon">↺</text>
<text class="cc-flip-label">翻转</text>
</view>
</view>
</view>
@ -37,18 +43,16 @@
</view>
</view>
<!-- 中间:拍照按钮 -->
<!-- 中间:拍照按钮(放大) -->
<view class="cc-footer-center">
<view class="cc-capture-outer">
<view class="cc-capture-btn" @click="capturePhoto"></view>
</view>
</view>
<!-- 右侧:翻转 -->
<!-- 右侧:完成按钮(拍完显示) -->
<view class="cc-footer-right">
<view class="cc-flip-btn" @click="flipCamera">
<text class="cc-flip-icon">↺</text>
</view>
<text class="cc-confirm-btn" @click="confirmCapture" v-if="capturedList.length > 0">完成</text>
</view>
</view>
@ -75,16 +79,14 @@
capturedList: [],
cameraReady: false,
facing: 'back',
winWidth: 750,
winHeight: 1334
camSize: 750
}
},
onLoad() {
var that = this
uni.getSystemInfo({
success: function(res) {
that.winWidth = res.windowWidth
that.winHeight = res.windowHeight
that.camSize = res.windowWidth
}
})
},
@ -119,7 +121,6 @@
},
deletePhoto(idx) {
this.capturedList.splice(idx, 1)
// 删完如果为0缩略图自动隐藏
},
goBack() {
if (this.capturedList.length > 0) {
@ -151,13 +152,19 @@
</script>
<style>
/* ─── 页面容器 ─── */
.cc-page {
flex: 1;
background-color: #000000;
align-items: center;
}
/* ─── 顶部栏 ─── */
/* 正方形相机区域,居中显示 */
.cc-camera-box {
margin-top: 110px;
background-color: #111111;
}
/* ═══ 顶部栏 ═══ */
.cc-topbar {
position: absolute;
top: 0; left: 0; right: 0;
@ -166,8 +173,8 @@
flex-direction: row;
align-items: center;
justify-content: space-between;
padding-left: 20rpx;
padding-right: 20rpx;
padding-left: 24rpx;
padding-right: 24rpx;
z-index: 8;
background: linear-gradient(to bottom, rgba(0,0,0,0.5), transparent);
}
@ -175,15 +182,15 @@
width: 90rpx;
}
.cc-topbar-back {
width: 56rpx; height: 56rpx;
border-radius: 56rpx;
background-color: rgba(0,0,0,0.3);
width: 60rpx; height: 60rpx;
border-radius: 60rpx;
background-color: rgba(0,0,0,0.35);
align-items: center; justify-content: center;
}
.cc-back-icon {
color: #ffffff;
font-size: 32rpx;
line-height: 56rpx;
font-size: 34rpx;
line-height: 60rpx;
text-align: center;
}
.cc-topbar-center {
@ -192,28 +199,42 @@
}
.cc-topbar-title {
color: #ffffff;
font-size: 30rpx;
font-size: 32rpx;
font-weight: 500;
}
.cc-topbar-right {
width: 120rpx;
align-items: flex-end;
}
.cc-topbar-confirm {
color: #409eff;
font-size: 28rpx;
font-weight: bold;
padding-left: 24rpx; padding-right: 24rpx;
padding-top: 10rpx; padding-bottom: 10rpx;
/* 翻转按钮(在右上角) */
.cc-flip-btn {
width: 80rpx; height: 64rpx;
border-radius: 32rpx;
background-color: rgba(64,158,255,0.15);
border-width: 2rpx;
border-color: rgba(255,255,255,0.3);
border-style: solid;
align-items: center; justify-content: center;
background-color: rgba(0,0,0,0.25);
}
.cc-flip-icon {
color: #ffffff;
font-size: 30rpx;
line-height: 36rpx;
text-align: center;
}
.cc-flip-label {
color: rgba(255,255,255,0.7);
font-size: 18rpx;
line-height: 22rpx;
text-align: center;
}
/* ─── 底部操作栏 ─── */
/* ═══ 底部操作栏 ═══ */
.cc-footer {
position: absolute;
bottom: 0; left: 0; right: 0;
height: 220rpx;
height: 240rpx;
padding-bottom: 40rpx;
flex-direction: row;
align-items: center;
@ -224,7 +245,7 @@
background: linear-gradient(to top, rgba(0,0,0,0.6), transparent);
}
.cc-footer-left {
width: 100rpx;
width: 120rpx;
align-items: flex-start;
}
.cc-footer-center {
@ -233,49 +254,43 @@
justify-content: center;
}
.cc-footer-right {
width: 100rpx;
width: 120rpx;
align-items: flex-end;
}
/* ─── 拍照按钮 ─── */
/* 拍照按钮(放大) */
.cc-capture-outer {
width: 90rpx; height: 90rpx;
border-radius: 90rpx;
width: 100rpx; height: 100rpx;
border-radius: 100rpx;
border-width: 4rpx;
border-color: rgba(255,255,255,0.6);
border-style: solid;
align-items: center; justify-content: center;
}
.cc-capture-btn {
width: 80rpx; height: 80rpx;
border-radius: 80rpx;
width: 88rpx; height: 88rpx;
border-radius: 88rpx;
background-color: #ffffff;
}
.cc-capture-btn:active {
background-color: #dddddd;
}
/* ─── 翻转按钮 ─── */
.cc-flip-btn {
width: 72rpx; height: 72rpx;
border-radius: 72rpx;
border-width: 2rpx;
border-color: rgba(255,255,255,0.35);
border-style: solid;
align-items: center; justify-content: center;
background-color: rgba(255,255,255,0.08);
}
.cc-flip-icon {
/* 完成按钮(在底部右侧) */
.cc-confirm-btn {
color: #ffffff;
font-size: 34rpx;
line-height: 72rpx;
text-align: center;
font-size: 30rpx;
font-weight: bold;
padding-left: 30rpx; padding-right: 30rpx;
padding-top: 14rpx; padding-bottom: 14rpx;
border-radius: 40rpx;
background-color: #409eff;
}
/* ─── 缩略图预览(最新一张) ─── */
/* 缩略图预览(最新一张) */
.cc-thumb-preview {
width: 72rpx; height: 72rpx;
border-radius: 8rpx;
width: 80rpx; height: 80rpx;
border-radius: 10rpx;
overflow: hidden;
position: relative;
border-width: 2rpx;
@ -283,49 +298,48 @@
border-style: solid;
}
.cc-thumb-last {
width: 72rpx; height: 72rpx;
width: 80rpx; height: 80rpx;
}
.cc-thumb-badge {
position: absolute;
top: -6rpx; right: -6rpx;
min-width: 28rpx; height: 28rpx;
border-radius: 28rpx;
min-width: 30rpx; height: 30rpx;
border-radius: 30rpx;
background-color: #409eff;
color: #ffffff;
font-size: 18rpx;
font-size: 20rpx;
text-align: center;
line-height: 28rpx;
padding-left: 4rpx;
padding-right: 4rpx;
line-height: 30rpx;
padding-left: 4rpx; padding-right: 4rpx;
}
/* ─── 缩略图横条 ─── */
/* 缩略图横条 */
.cc-thumb-bar {
position: absolute;
bottom: 240rpx; left: 0; right: 0;
bottom: 260rpx; left: 0; right: 0;
padding-left: 24rpx; padding-right: 24rpx;
padding-top: 10rpx; padding-bottom: 10rpx;
padding-top: 14rpx; padding-bottom: 14rpx;
z-index: 8;
}
.cc-thumb-item {
width: 110rpx; height: 110rpx;
border-radius: 8rpx;
width: 120rpx; height: 120rpx;
border-radius: 10rpx;
overflow: hidden;
position: relative;
border-width: 2rpx;
border-color: rgba(255,255,255,0.3);
border-style: solid;
margin-right: 14rpx;
margin-right: 16rpx;
background-color: #333333;
}
.cc-thumb-img {
width: 110rpx; height: 110rpx;
width: 120rpx; height: 120rpx;
}
.cc-thumb-del {
position: absolute;
top: -8rpx; right: -8rpx;
width: 36rpx; height: 36rpx;
border-radius: 36rpx;
width: 40rpx; height: 40rpx;
border-radius: 40rpx;
background-color: rgba(0,0,0,0.55);
align-items: center; justify-content: center;
border-width: 2rpx;
@ -334,6 +348,6 @@
}
.cc-del-icon {
color: #ffffff;
font-size: 20rpx;
font-size: 22rpx;
}
</style>

View File

@ -122,11 +122,11 @@
</view>
<view class="photo-add" @click="takePhoto" v-if="photoList.length < 9">
<text class="add-icon">+</text>
<text class="add-text"></text>
<text class="add-text"></text>
</view>
<view class="photo-add photo-burst" @click="openCameraCapture('isbn')" v-if="photoList.length < 9">
<text class="add-icon" style="font-size:28rpx;"></text>
<text class="add-text"></text>
<text class="add-text"></text>
</view>
</view>
</view>
@ -443,11 +443,11 @@
</view>
<view class="photo-add" @click="takePhotoNoIsbn" v-if="noIsbnPhotoList.length < 9">
<text class="add-icon">+</text>
<text class="add-text"></text>
<text class="add-text"></text>
</view>
<view class="photo-add photo-burst" @click="openCameraCapture('no-isbn')" v-if="noIsbnPhotoList.length < 9">
<text class="add-icon" style="font-size:28rpx;"></text>
<text class="add-text"></text>
<text class="add-text"></text>
</view>
</view>
</view>

View File

@ -0,0 +1,438 @@
## 1.2.22026-06-11
> 新增功能
- 1、**iOS 端补齐 `previewRotation` 支持**`index.vue` 新增 `previewRotation` prop 与 `changePreviewRotation` 方法,`index.uts` 导出 `setPreviewRotation``ImaCameraManager.swift` 通过 `CGAffineTransform(rotationAngle:)` 实现预览层旋转(与 Android 行为对齐)。
> 问题修复
- 1、**修复首次进入时 `previewRotation` 初始值 90° 无效**`initCameraView()` 现在始终使用 `TextureView` 预览模式(不再等 `previewRotationDegrees != 0f` 才切换),避免初始化时用 `SurfaceView`、后续 prop 到达时 `setPreview(TEXTURE)` 触发相机重启导致旋转丢失。
- 2、**修复 `setPreviewRotation(0)` 不生效**`applyPreviewRotation()` 移除 `previewRotationDegrees == 0f` 提前返回0° 时显式重置 `cv.rotation = 0f``scaleX/Y = 1f`
- 3、**修复旋转切换按钮点击无效**`NVLoad()` 中在 `initCameraView()` 之前调用 `setPreviewRotation(this.previewRotation)`,确保原生 CameraView 创建时已读取旋转角度;`NVLayouted()` 增加安全网再次应用旋转。
- 4、**修复首次进入时 `onCameraOpened``$refs.cameraRef` 可能未就绪**:使用 `$nextTick` 延迟调用 `applyPreviewRotation()`picture→video 模式切换延迟从 300ms 延长至 800ms并在切换前同步调用一次旋转。
## 1.2.12026-06-10
> 新增功能
- 1、**外接摄像头预览旋转修正Android**:新增 `previewRotation` 属性及 `changePreviewRotation` 方法,用于修正无内置摄像头设备(如平板)外接 USB 摄像头时预览画面方向错误的问题;支持 `0` / `90` / `180` / `270`(或 `-90` 等等效角度)。
- 2、设备旋转时自动保持预览修正方向不变`onCameraOpened`、`onOrientationChanged` 时重新应用)。
> 问题修复
- 1、修复设置 `previewRotation` 后预览已正常、但**拍照输出图片方向仍不正确**的问题:拍照时会将 `previewRotation` 叠加到原生 `result.rotation` 后一并修正。
> 功能优化
- 1、启用 `previewRotation` 时自动切换为 `TextureView` 预览模式,并通过 `CameraView` 视图层旋转 + 缩放填充实现稳定预览(不依赖易被原生覆盖的 `TextureView.setTransform`)。
> 平台说明
- 本能力目前仅在 **`android`** 端实现;`ios` / `harmony` 暂未提供同等 API。
> 由于 **`harmony端`** 不支持兼容性组件,`harmony端`(指的是`harmony 5.0+`)不再本组件中更新,插件正在更新中
## 1.2.02026-05-24
> 新增功能
- 无
> 问题修复
- 无
> 功能优化
- 无
> 由于 **`harmony端`** 不支持兼容性组件,`harmony端`(指的是`harmony 5.0+`)不再本组件中更新,插件正在更新中
## 1.1.162026-05-20
> 新增功能
- 1、**预览圆角**:支持 `previewCornerRadius`、`previewCornerRadiusRate` 及 `changePreviewCorner`(圆形/多档圆角预览以原生实现为准)。
> 问题修复
- 1、修复「同页先预热 `video` 再轻触拍照」时,若在原生 `opened` 回调中无条件按 prop 再次 `changeMode`,会与原生「为拍照切换到 PICTURE 再在下一帧 `takePicture`」的逻辑冲突,导致预览反复关闭/打开、拍照异常的问题(已移除该盲目同步)。
- 2、`nvue` / 部分运行时下 `$refs` 实例上 **`changeMode` 等方法不可用** 的问题:通过 **`mode` prop + `watch`** 同步原生模式,页面侧推荐使用 `:mode` 切换 `picture` / `video`
> 功能优化
- 1、**`mode` prop** 增加监听:变更时调用原生 `setMode`,与 `changeMode` 行为对齐。
- 2、录像相关停止录像、开始时间戳等逻辑在主线程/时序上做了加固。
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
## 1.1.152026-02-02
> 新增功能(临时更新)
- 无
> 问题修复
- 1、部分手机无法录像的问题或者录像中没有返回结果的问题可能解决`{"errorCode"=>5,"reason"=>"录像失败","message"=>"java.lang.RuntimeException:startfailed."}`的问题)
- 1.1、增加了待录像(切换 VIDEO 模式后延后执行)
- 1.2、待拍照(切换 PICTURE 模式后延后执行)
> 功能优化
- 1、增加拍照、录像、结束录像时异常抛出
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
## 1.1.142026-01-30
> 新增功能(临时更新)
- 无
> 问题修复
- 1、部分手机无法录像的问题或者录像中没有返回结果的问题
- 2、修改代码结构准备改成兼容组件模式为`Harmony Next`做准备
> 功能优化
- 无
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
## 1.1.132026-01-22
> 新增功能(临时更新)
- 无
> 问题修复
- 1、修复获取Activity实例、Application上下文上下文出现的问题
- 2、修复相机按键、蓝牙自拍杆监听事件的问题
> 功能优化
- 1、相机震动做了兼容处理
- 2、相机的权限申请做了兼容处理
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
## 1.1.122026-01-19
> 新增功能(临时更新)
- 无
> 问题修复
- 1、修复安卓8.1事打开相机报错“相机初始失败或者当前设备不支持创建失败When targetSdkVersion >= 33
should use amdroid.permission.xxx,...”
> 功能优化
- 无
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
## 1.1.112026-01-07
> 新增功能(临时更新)
- 1、增加`take-error相机拍摄监测`、`orientation-change相机角度转换`、、`camera-change相机设置监听`
> 问题修复
- 1、在`setSizeSelectors`方法增加错误回调,便于排查一些手机调取无反应的问题
> 功能优化
- 无
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
## 1.1.102026-01-07
> 新增功能(临时更新)
- 1、增加`take-error相机拍摄监测`、`orientation-change相机角度转换`、、`camera-change相机设置监听`
> 问题修复
- 1、在`setSizeSelectors`方法增加错误回调,便于排查一些手机调取无反应的问题
> 功能优化
- 无
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
## 1.1.92026-01-06
> 新增功能(临时更新)
- 1、新增加载相机基础参数方法`loadCameraView`
> 问题修复
- 1、修复在 `HBuilderX 4.76` 及以下版本打包包括自定义基座在kotlin文件中无法获取当前
`UTSAndroid.getUniActivity()``UTSAndroid.getAppContext()`的问题
- 2、修复在 `HBuilderX 4.76` 及以下版本打包(包括自定义基座)时白屏后闪退的问题
- 3、修复高版本`安卓15+`及以上获取的SdkVersion值与当前应用实际的SdkVersion值不一致导致相机获取权限崩溃后闪退的问题
> 功能优化
- 无
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
## 1.1.82026-01-05
> 新增功能(临时更新)
- 1、是否将拍摄文件保存到本地可见媒体即相册的方法`changeGallery`
> 问题修复
- 1、相机权限的问题如用户拒绝后可直接跳转到当前 App 的系统设置页
> 功能优化
- 无
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
## 1.1.72026-01-04
> 新增功能(临时更新)
- 无
> 问题修复
- 1.1.6 版本推包后上传不成功的问题下载和导入还是1.1.5的代码)
> 功能优化
- 无
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
## 1.1.62026-01-04
> 新增功能(临时更新)
- 无
> 问题修复
- 1、安卓高版本安卓16出现拍照的问题
- 2、在一些其他终端设备非手机中出现相机预览画面与实际不符合的问题`【待复测】`
- 3、一些配置不生效的问题如changeAspectRatio、changeOrientation由于相机加载顺序问题导致的
- 4、一些其他终端设备非手机进入卡顿或者黑屏几秒才显示相机预览页面`【待复测】`
- 5、一些手机设备由于权限问题导致第一次进入是黑屏或者后几次进入偶尔出现黑屏闪现的问题`【待复测】`
> 功能优化
- 1、将之前的uts改成了kotlin的写法
- 2、将组件是的抛出调整成`CameraManager.setCameraCallback`,方便后期增加参数输出、控制
- 3、优化了相机加载的顺序问题、以及设置相机时的问题
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
## 1.1.52025-12-29
> 新增功能(临时更新)
- 新增设置相机使用设备方向的方法`changeOrientation`
- 新增设置相机网格及颜色的方法`changeGrid`
> 问题修复
- 无
> 功能优化
- 无
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
- 完善鸿蒙`Harmony`版本的并进行更新(已完成开发,测试中)
## 1.1.42025-12-28
> 新增功能(临时更新)
- 新增设置曝光值的方法`changeExposure`
> 问题修复
- 修复动态权限申请的问题即Android 12 及以下、Android 13+
- 修复 Android 16(即API级别为36)时摄像回调的问题
> 功能优化
- 无
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
- 完善鸿蒙`Harmony`版本的并进行更新
## 1.1.32025-12-02
> 新增功能(临时更新)
- 无
> 问题修复
- 打包报错问题修复4.85版本有问题4.76以下的版本未复现)
- 报错内容:
`ima-camera-view/utssdk/app-android/src/index.kt:723:55 Argument type mismatch: actual type is 'Any', but 'Number' was expected`
> 功能优化
- 无
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
- 完善鸿蒙`Harmony`版本的并进行更新
## 1.1.22025-11-06
> 新增功能(临时更新)
- 无
> 问题修复
- `快捷键拍照(蓝牙自拍杆、手机音量键)`拍照时,返回后按钮无法操作的问题修复
- 页面在没启用`shortcut快捷键拍照`时,也会进入快捷键的问题修复
- 修复在低版本`HubuildX`时打包出现找不到类型的问题
> 功能优化
- 优化了相机资源在进入时卡顿的问题
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
- 完善鸿蒙`Harmony`版本的并进行更新
## 1.1.12025-11-03
> 新增功能(临时更新)
- 新增设置照片输出格式的方法`changeSuffix`
> 问题修复
- 修复照片格式无法设置的问题【原因是在uni给出的生命周期内无法取到`props`的参数,只能在`watch`中处理】
> 功能优化
- 无
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
- 完善鸿蒙`Harmony`版本的并进行更新
## 1.1.02025-10-31
> 新增功能
- 快捷键拍照: 如:按两下音量键拍照、按音量键拍照等等(可自定义)
- 蓝牙自拍杆: 提供可以连接蓝牙自拍杆拍照、对焦等(可自定义)
- 相机`是否开启蓝牙自拍杆、手机快捷键拍照`,可自定义快捷键,具体参考参数`shortcut`
-
> 问题修复
- 无
> 功能优化
- 无
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
- 完善鸿蒙`Harmony`版本的并进行更新
## 1.0.52025-10-30
> 新增功能(临时更新)
- 相机`录像`方法增加设置`视频录制时长限制`
- 相机`录像`声音默认系统录像声音,也可自定义,具体参考参数`recorder`、`sound2`
> 问题修复
- 无
> 功能优化
- 无
## 1.0.42025-10-18
> 新增功能(临时更新)
- 增加设置相机`白平衡`方法:`changeWhiteBalance`
- 增加设置相机`HDR`方法:`changeHdr`
- 增加设置相机`特定比例`方法:`changeAspectRatio`
- 新增了`widthRatio`、`heightRatio`、`tolerance`、`whiteBalance`、`hdr`、`shutter`、`sound`、`vibrate`、
`duration`等参数具体参考API
> 问题修复
- 无
> 功能优化
- 照片文件的尺寸和手机原相机的尺寸不对的问题,可以通过参数`duration`去控制,其中`全屏`、`1:1`
基本是和原相机尺寸一致的测试了大部分手机型号都OK个别手机`3:4`、`4:3`、`9:16`时会有少许容差,可以通过参数
`duration`去控制)
## 1.0.32025-10-16
> 新增功能
- 增加拍照声音,可以自定义声音文件(默认手机原相机声音)
- 增加拍照震动可以自定义震动时长默认200毫秒
> 问题修复
> 无
> 功能优化
- 照片文件的分辨率取手机原相机的,提高照片的清晰度
## 1.0.22025-09-09
> 新增功能
> 无
> 问题修复
> 无
> 功能优化
- 支持拍照预览或从相册选择后,返回当前相机页面时,如果出现黑屏状态,可以重新自己手动拉起相机
## 1.0.12025-09-05
> 新增功能
> 无
> 问题修复
- 修复高版本(`4.66`时)打包报错:`*(项目路径)*//index.kt:44:12 Unresolved reference: _uA`、
`*(项目路径)*/index.kt:118:48 Unresolved reference: _uO`的问题
- 修复前置拍出来的图片是镜像的问题
- 修复照片拍出来尺寸不对、照片过小的问题
> 功能优化
> 无
## 1.0.02025-05-27
> 新增功能
- 新增方法:`close`、 `open``takePhoto``takePhotoSnapshot``takeVideo`、`takeVideoSnapshot`、
`stopVideo``changeZoom``changeFacing`、`changeFlash`、`changeAudio`
- 新增事件:`onPictureTaken`、 `onVideoTakenStart``onVideoTakenEnd``onFocusStart``onFocusEnd`
- `android端`的所有功能已完成开发、测试
- `harmony端`计划开发中
- 初始版
> 问题修复
> 无
> 功能优化
> 无

Binary file not shown.

View File

@ -0,0 +1,126 @@
{
"id": "ima-camera-view",
"displayName": "原生Camera自定义相机拍照、视频录制",
"version": "1.2.2",
"description": "原生Camera相机开发的UTS插件支持相机拍照、视频录制、可实现点击聚焦、手势缩放、自定义布局、自定义蒙版用于人脸拍照身份证拍照等同时支持蓝牙自拍杆可自定义、手机快捷键可自定义",
"keywords": [
"原生Camera",
"相机拍照",
"视频录制",
"自定义布局相机",
"自定义蒙版相机"
],
"repository": "",
"engines": {
"HBuilderX": "^4.8.1",
"uni-app": "^4.81",
"uni-app-x": "^4.81"
},
"dcloudext": {
"type": "component-uts",
"sale": {
"regular": {
"price": "19.99"
},
"sourcecode": {
"price": "299.00"
}
},
"contact": {
"qq": "488266488"
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "摄像头、音频、文件读取、文件写入\n <uses-permission android:name=\"android.permission.CAMERA\" />\n <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n <uses-permission android:name=\"android.permission.VIBRATE\" />"
},
"npmurl": "",
"darkmode": "x",
"i18n": "x",
"widescreen": "√"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "x",
"aliyun": "x",
"alipay": "x"
},
"client": {
"uni-app": {
"vue": {
"vue2": {
"extVersion": "1.1.13",
"minVersion": ""
},
"vue3": {
"extVersion": "1.1.13",
"minVersion": ""
}
},
"web": {
"safari": "x",
"chrome": "x"
},
"app": {
"vue": {
"extVersion": "1.1.13",
"minVersion": ""
},
"nvue": {
"extVersion": "1.1.13",
"minVersion": ""
},
"android": {
"extVersion": "1.1.13",
"minVersion": "21"
},
"ios": "x",
"harmony": {
"extVersion": "1.1.13",
"minVersion": "5.0以下(不包括5.0)"
}
},
"mp": {
"weixin": "x",
"alipay": "x",
"toutiao": "x",
"baidu": "x",
"kuaishou": "x",
"jd": "x",
"harmony": "x",
"qq": "x",
"lark": "x",
"xhs": "-"
},
"quickapp": {
"huawei": "x",
"union": "x"
}
},
"uni-app-x": {
"web": {
"safari": "x",
"chrome": "x"
},
"app": {
"android": {
"extVersion": "1.1.13",
"minVersion": "21"
},
"ios": "x",
"harmony": {
"extVersion": "1.1.13",
"minVersion": "5.0以下(不包括5.0)"
}
},
"mp": {
"weixin": "x"
}
}
}
}
}
}

View File

@ -0,0 +1,373 @@
# 原生自定义相机拍照、视频录制 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。
## 常见的比例的定义widthRatioheightRatio
```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为9heightRatio为16则为9:16、widthRatio为3heightRatio为4则为3:4...不建议值过大常用的比例有1:1、3:4、4:3、9:16... | `android` |
| heightRatio | Number | 0 | 照片尺寸比率(高度): 默认全屏若widthRatio为9heightRatio为16则为9:16、widthRatio为3heightRatio为4则为3:4...不建议值过大常用的比例有1:1、3:4、4:3、9:16... | `android` |
| tolerance | Number | 0.1 | 照片尺寸容差值: 建议设为 0.05~0.15,以便稍微兼容不同设备相机实际比例差异 ,值为:01 | `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:曝光值(浮点数)【值为:-22默认为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: 圆角 pxradiusRate: 圆角比例(正方形下 `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` |

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
package="uts.sdk.modules.imaCameraView">
<!-- 相机权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 震动权限 -->
<uses-permission android:name="android.permission.VIBRATE" />
<!-- 录音权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Android 13+ -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<!-- Android 12 及以下 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 功能声明 -->
<uses-feature android:name="android.hardware.camera" android:required="true"/>
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:name="android.hardware.camera.flash" />
</manifest>

View File

@ -0,0 +1,9 @@
{
"minSdkVersion": 21,
"dependencies": [
{
"id": "com.otaliastudios:cameraview",
"source": "implementation('com.otaliastudios:cameraview:2.7.2') { exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk7'; exclude group: 'androidx.core', module: 'core' }"
}
]
}

View File

@ -0,0 +1,651 @@
<template>
<view>
<slot />
</view>
</template>
<script lang="uts">
import FrameLayout from 'android.widget.FrameLayout';
import { ListenerOptions } from 'com.maxiaoqu.camera'
import {
initCameraView, setCameraCallback,
applyPermission, openAppSettings, vibrate, shutterSound, recordSound, keyDestroyer, keyListener,
open, reopen, close, destroyCamera, takePhoto, takePhotoSnapshot, takeVideo, takeVideoSnapshot, stopVideo,
setZoom, setExposure, setWhiteBalance, setHdr, setFacing, setGrid, setFlash, setAudio, setSuffix, setGallery, setOrientation, setMode, setSizeSelectors,
setPreviewCorner, setPreviewRotation, isTakingVideo, isTakingPicture, isOpened
} from './index.uts'
export default {
name: "ima-camera-view",
props: {
mode: {
type: String,
default: 'picture' // video
},
widthRatio: {
type: Number,
default: 0
},
heightRatio: {
type: Number,
default: 0
},
tolerance: {
type: Number,
default: 0.1
},
previewCornerRadius: {
type: Number,
default: 0
},
previewCornerRadiusRate: {
type: Number,
default: 0
},
previewRotation: {
type: Number,
default: 0
},
whiteBalance: {
type: String,
default: 'auto'
},
hdr: {
type: String,
default: 'off'
},
facing: {
type: String,
default: 'back'
},
flash: {
type: String,
default: 'off'
},
audio: {
type: String,
default: 'on'
},
photoSuffix: {
type: String,
default: 'jpeg'
},
orientation: {
type: String,
default: 'auto'
},
grid: {
type: String,
default: 'off'
},
gridColor: {
type: String,
default: '#808080'
},
shutter: {
type: Boolean,
default: true
},
sound: {
type: String,
default: ''
},
recorder: {
type: Boolean,
default: true
},
sound2: {
type: String,
default: ''
},
vibrate: {
type: Boolean,
default: false
},
duration: {
type: Number,
default: 300
},
gallery: {
type: Boolean,
default: false
},
shortcut: {
type: Boolean,
default: false
},
},
data() {
return {
cameraZoom: 0,
cameraFacing: 'back',
cameraViewActivity: null,
cameraViewContext: null,
}
},
watch: {
mode: {
handler(newValue : String) {
this.changeMode(newValue)
},
immediate: false
},
whiteBalance: {
handler(newValue : String) {
this.changeWhiteBalance(newValue)
},
immediate: true
},
hdr: {
handler(newValue : String) {
this.changeHdr(newValue)
},
immediate: true
},
facing: {
handler(newValue : String) {
this.cameraFacing = newValue;
this.changeFacing(newValue)
},
immediate: false
},
flash: {
handler(newValue : String) {
this.changeFlash(newValue)
},
immediate: false
},
audio: {
handler(newValue : String) {
this.changeAudio(newValue)
},
immediate: false
},
photoSuffix: {
handler(newValue : String) {
this.changeSuffix(newValue)
},
immediate: true
},
orientation: {
handler(newValue : String) {
this.changeOrientation(newValue)
},
immediate: true
},
grid: {
handler(newValue : String) {
this.changeGrid(newValue, '#808080')
},
immediate: true
},
gallery: {
handler(newValue : Boolean) {
this.changeGallery(newValue)
},
immediate: true
},
previewCornerRadius: {
handler(newValue ?: Number) {
this.changePreviewCorner(newValue, this.previewCornerRadiusRate)
},
immediate: true
},
previewCornerRadiusRate: {
handler(newValue ?: Number) {
this.changePreviewCorner(this.previewCornerRadius, newValue)
},
immediate: true
},
previewRotation: {
handler(newValue : Number) {
this.changePreviewRotation(newValue)
},
immediate: true
},
shortcut: {
handler(newValue : Boolean) {
this.shortcutListener()
},
immediate: true
}
},
/**
* 组件涉及的事件声明只有声明过的事件才能被正常发送
*/
emits: [
'onCameraOpened',
'onCameraClosed',
'onPictureTaken',
'onVideoTakenStart',
'onVideoTakenProgress',
'onVideoTakenEnd',
'onFocusStart',
'onFocusEnd',
'onZoomChanged',
'onCameraChange',
'onOrientationChange',
'onCameraTakenError',
'onCameraError',
],
/**
* 规则如果没有配置expose则methods中的方法均对外暴露如果配置了expose则以expose的配置为准向外暴露
* ['publicMethod'] 含义为只有 `publicMethod` 在实例上可用
*/
expose: [
'open',
'reopen',
'close',
'destroyCamera',
'takePhoto',
'takePhotoSnapshot',
'takeVideo',
'takeVideoSnapshot',
'stopVideo',
'changeZoom',
'changeExposure',
'changeWhiteBalance',
'changeHdr',
'changeFacing',
'changeMode',
'changeOrientation',
'changeGrid',
'changeFlash',
'changeAudio',
'changeSuffix',
'changeSizeSelectors',
'changePreviewCorner',
'changePreviewRotation',
'changeGallery',
'openAppSettings'
],
methods: {
//
initCameraView(): FrameLayout{
return initCameraView((success : boolean, message : string) => {
if (success) {
this.changePreviewCorner(this.previewCornerRadius, this.previewCornerRadiusRate);
this.initCamera();
} else {
console.warn(`相机初始失败或者当前设备不支持:${message}`);
}
})
},
//
initCameraPermission() {
applyPermission((allRight : boolean, grantedList : Array<String>) => {
//
if (allRight) {
this.initCameraView();
console.log(`用户同意了全部权限: ${grantedList}`);
}
// grantedList
else {
console.warn(`部分权限被拒绝: ${grantedList}`);
}
}, (doNotAskAgain : boolean, grantedList : Array<String>) => {
//
if (doNotAskAgain) {
// App
openAppSettings()
console.warn(`权限被永久拒绝: ${grantedList}`);
}
})
},
openAppSettings(){
openAppSettings()
},
//
initCamera() {
setCameraCallback((event : string, data : any) => {
switch (event) {
case "opened":
console.log("相机已打开", data)
this.$emit('onCameraOpened', data)
break
case "closed":
console.log("相机已关闭", data)
this.$emit('onCameraClosed', data)
break
case "picture":
console.log("照片", data)
this.$emit('onPictureTaken', data)
break
case "video-start":
console.log("开始录制", data)
this.$emit('onVideoTakenStart', data)
break
case "video-progress":
console.log("录制中", data)
this.$emit('onVideoTakenProgress', data)
break
case "video-end":
console.log("结束视频", data)
// this.$emit('onVideoTakenEnd', data)
break
case "video":
console.log("视频资源", data)
this.$emit('onVideoTakenEnd', data)
this.$emit('onVideoTaken', data)
break
case "focus-start":
console.log("对焦开始", data)
this.$emit('onFocusStart', data)
break
case "focus-end":
console.log("对焦结束", data)
this.$emit('onFocusEnd', data)
break
case "zoom-change":
console.log("缩放级别", data)
this.$emit('onZoomChanged', data)
break
case "camera-change":
console.log("相机设置", data)
this.$emit('onCameraChange', data)
break
case "orientation-change":
console.log("相机角度转换", data)
this.$emit('onOrientationChange', data)
break
case "take-error":
console.log("相机拍摄监测", data)
this.$emit('onCameraTakenError', data)
break
case "error":
console.log("相机出错", data)
this.$emit('onCameraError', data)
break
}
})
open();
},
//
open() {
open();
},
//
reopen() {
reopen();
},
//
close() {
close();
},
//
destroyCamera() {
if (this.$el != null) {
destroyCamera()
}
},
//
takePhoto() {
this.photoSound()
takePhoto()
},
//
takePhotoSnapshot() {
this.photoSound()
takePhotoSnapshot()
},
//
takeVideo(duration ?: Number) {
const videoDuration : Number = duration ?? 0;
this.videoSound(true)
takeVideo(videoDuration)
},
//
takeVideoSnapshot(duration ?: Number) {
const videoDuration : Number = duration ?? 0;
this.videoSound(true)
takeVideoSnapshot(videoDuration)
},
//
stopVideo() {
stopVideo();
},
// zoom
changeZoom(zoom : Number) {
setZoom(zoom)
},
// exposure
changeExposure(exposure : Number) {
setExposure(exposure)
},
//
changeWhiteBalance(whiteBalance : String) {
setWhiteBalance(whiteBalance)
},
// HDR
changeHdr(hdr : String) {
setHdr(hdr)
},
//
changeFacing(facing : String) {
setFacing(facing)
},
//
changeMode(mode : String) {
setMode(mode)
},
//
changeGrid(grid : String, color : String) {
setGrid(grid, color)
},
//
changeFlash(flash : String) {
setFlash(flash)
},
//
changeAudio(audio : String) {
setAudio(audio)
},
//
changeSuffix(suffix : String) {
setSuffix(suffix)
},
//
changeGallery(gallery : Boolean) {
setGallery(gallery)
},
// 使
changeOrientation(orientation : String) {
setOrientation(orientation)
},
//
changeSizeSelectors(width : Number, height : Number, tolerance : Number) {
setSizeSelectors(width, height, tolerance)
},
// radiusRate=0.5
changePreviewCorner(radius ?: Number, radiusRate ?: Number) {
let safeRadius : Number = 0
let safeRadiusRate : Number = 0
if (radius != null) {
safeRadius = radius
}
if (radiusRate != null) {
safeRadiusRate = radiusRate
}
setPreviewCorner(safeRadius, safeRadiusRate)
},
// 0/90/180/270
changePreviewRotation(degrees : Number) {
setPreviewRotation(degrees)
},
//
isTakingVideo() : Boolean {
return isTakingVideo();
},
//
isTakingPicture() : Boolean {
return isTakingPicture();
},
//
isOpened() : Boolean {
return isOpened();
},
//
photoSound() {
if (this.vibrate) {
vibrate(this.duration ?? 300)
}
if (this.shutter) {
shutterSound(this.sound ?? '')
}
},
//
videoSound(isStart : Boolean) {
if (this.recorder) {
recordSound(isStart, this.sound2 ?? '')
}
},
//
shortcutListener() {
//
if (!this.shortcut) {
return
}
// DONE:
// actiondown()up()
const baseOptions : ListenerOptions = {
type: "base", // 使
intercept: true //
}
keyListener(baseOptions, (action : String, code : Number, name : String) => {
console.log(`按键事件: ${action} -> ${name} (${code})`)
//
if ((code == 24 || code == 25) && action == 'up') {
this.takePhoto();
}
// ()
else if (code == 168 && action == 'down') {
this.cameraZoom = this.cameraZoom + 0.01;
if (this.cameraZoom >= 1) {
this.cameraZoom = 1
}
console.log('聚焦(加)', this.cameraZoom)
this.changeZoom(this.cameraZoom)
}
// ()
else if (code == 169 && action == 'down') {
this.cameraZoom = this.cameraZoom - 0.01;
if (this.cameraZoom <= 0) {
this.cameraZoom = 0
}
console.log('聚焦(减)', this.cameraZoom)
this.changeZoom(this.cameraZoom)
}
//
else if (code == 119 && action == 'up') {
this.cameraFacing = this.cameraFacing == 'front' ? 'back' : 'front';
console.log('设置摄像头方向', this.cameraFacing)
this.changeFacing(this.cameraFacing)
}
});
// TODO:
// actiondown()up()single()double()long()
// const fullOptions: ListenerOptions = {
// type: "full", // 使
// intercept: true //
// doubleInterval: 400, // ms 400
// longPressTime; 500, // ms 500
// }
// keyListener(fullOptions, (action : string, code : number, name : string) => {
// console.log(`: ${action} -> ${name} (${code})`)
// });
}
},
/**
* [可选实现] 组件被创建组件第一个生命周期
* 在内存中被占用的时候被调用开发者可以在这里执行一些需要提前执行的初始化逻辑
*/
created() {
//
this.initCameraPermission()
},
NVBeforeLoad() {
},
/**
* [必须实现] 创建原生View必须定义返回值类型
* 开发者需要重点实现这个函数声明原生组件被创建出来的过程以及最终生成的原生组件类型
* Android需要明确知道View类型需特殊校验
*/
NVLoad() : FrameLayout {
// initCameraView CameraView
// previewRotationDegrees TextureView
// setPreview(Texture)
setPreviewRotation(this.previewRotation)
return this.initCameraView()
},
/**
* [可选实现] 原生View已创建
*/
NVLoaded() {
//
UTSAndroid.onAppActivityPause(() => {
console.log('自动关闭摄像头')
if (this.isOpened() == true) {
this.close();
}
});
//
UTSAndroid.onAppActivityResume(() => {
console.log('自动打开摄像头')
if (this.isOpened() != true) {
this.open();
}
});
},
/**
* [可选实现] 原生View布局完成
*/
NVLayouted() {
this.changePreviewCorner(this.previewCornerRadius, this.previewCornerRadiusRate);
// props
this.changePreviewRotation(this.previewRotation);
},
/**
* [可选实现] 原生View将释放
*/
NVBeforeUnload() {
},
/**
* [可选实现] 原生View已释放这里可以做释放View之后的操作
*/
NVUnloaded() {
//
if (this.$el != null) {
destroyCamera();
}
//
if (this.shortcut) {
keyDestroyer();
}
},
/**
* [可选实现] 组件销毁
*/
unmounted() {
//
if (this.$el != null) {
destroyCamera();
}
//
if (this.shortcut) {
keyDestroyer();
}
UTSAndroid.offAppActivityPause();
UTSAndroid.offAppActivityResume();
},
/**
* [可选实现] 自定组件布局尺寸用于告诉排版系统组件自身需要的宽高
* 一般情况下组件的宽高应该是由终端系统的排版引擎决定组件开发者不需要实现此函数
* 但是部分场景下组件开发者需要自己维护宽高则需要开发者重写此函数
*/
NVMeasure(size : UTSSize) : UTSSize {
return size;
}
}
</script>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSCameraUsageDescription</key>
<string>需要您的同意才能使用摄像头进行拍照和录像</string>
<key>NSMicrophoneUsageDescription</key>
<string>需要您的同意才能录制视频中的声音</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>需要您的同意才能将拍摄的照片和视频保存到相册</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>需要您的同意才能将拍摄的照片和视频保存到相册</string>
</dict>
</plist>

View File

@ -0,0 +1,10 @@
{
"deploymentTarget": "12.0",
"frameworks": [
"AVFoundation",
"Photos",
"UIKit",
"Foundation",
"AudioToolbox"
]
}

Binary file not shown.

View File

@ -0,0 +1,634 @@
<template>
<view>
<slot />
</view>
</template>
<script lang="uts">
import { UIView } from 'UIKit'
import {
initCameraView, setCameraCallback,
applyPermission, openAppSettings, vibrate, shutterSound, recordSound, keyDestroyer, keyListener,
open, reopen, close, destroyCamera, takePhoto, takePhotoSnapshot, takeVideo, takeVideoSnapshot, stopVideo,
setZoom, setExposure, setWhiteBalance, setHdr, setFacing, setGrid, setFlash, setAudio, setSuffix, setGallery, setOrientation, setMode, setSizeSelectors,
setPreviewCorner, setPreviewRotation, isTakingVideo, isTakingPicture, isOpened
} from './index.uts'
export default {
name: "ima-camera-view",
props: {
mode: {
type: String,
default: 'picture' // video
},
widthRatio: {
type: Number,
default: 0
},
heightRatio: {
type: Number,
default: 0
},
tolerance: {
type: Number,
default: 0.1
},
previewCornerRadius: {
type: Number,
default: 0
},
previewCornerRadiusRate: {
type: Number,
default: 0
},
previewRotation: {
type: Number,
default: 0
},
whiteBalance: {
type: String,
default: 'auto'
},
hdr: {
type: String,
default: 'off'
},
facing: {
type: String,
default: 'back'
},
flash: {
type: String,
default: 'off'
},
audio: {
type: String,
default: 'on'
},
photoSuffix: {
type: String,
default: 'jpeg'
},
orientation: {
type: String,
default: 'auto'
},
grid: {
type: String,
default: 'off'
},
gridColor: {
type: String,
default: '#808080'
},
shutter: {
type: Boolean,
default: true
},
sound: {
type: String,
default: ''
},
recorder: {
type: Boolean,
default: true
},
sound2: {
type: String,
default: ''
},
vibrate: {
type: Boolean,
default: false
},
duration: {
type: Number,
default: 300
},
gallery: {
type: Boolean,
default: false
},
shortcut: {
type: Boolean,
default: false
},
},
data() {
return {
cameraZoom: 0,
cameraFacing: 'back',
cameraViewActivity: null,
cameraViewContext: null,
}
},
watch: {
mode: {
handler(newValue : String) {
this.changeMode(newValue)
},
immediate: false
},
whiteBalance: {
handler(newValue : String) {
this.changeWhiteBalance(newValue)
},
immediate: true
},
hdr: {
handler(newValue : String) {
this.changeHdr(newValue)
},
immediate: true
},
facing: {
handler(newValue : String) {
this.cameraFacing = newValue;
this.changeFacing(newValue)
},
immediate: false
},
flash: {
handler(newValue : String) {
this.changeFlash(newValue)
},
immediate: false
},
audio: {
handler(newValue : String) {
this.changeAudio(newValue)
},
immediate: false
},
photoSuffix: {
handler(newValue : String) {
this.changeSuffix(newValue)
},
immediate: true
},
orientation: {
handler(newValue : String) {
this.changeOrientation(newValue)
},
immediate: true
},
grid: {
handler(newValue : String) {
this.changeGrid(newValue, '#808080')
},
immediate: true
},
gallery: {
handler(newValue : Boolean) {
this.changeGallery(newValue)
},
immediate: true
},
previewCornerRadius: {
handler(newValue ?: Number) {
this.changePreviewCorner(newValue, this.previewCornerRadiusRate)
},
immediate: true
},
previewCornerRadiusRate: {
handler(newValue ?: Number) {
this.changePreviewCorner(this.previewCornerRadius, newValue)
},
immediate: true
},
previewRotation: {
handler(newValue : Number) {
this.changePreviewRotation(newValue)
},
immediate: true
},
shortcut: {
handler(newValue : Boolean) {
this.shortcutListener()
},
immediate: true
}
},
/**
* 组件涉及的事件声明只有声明过的事件才能被正常发送
*/
emits: [
'onCameraOpened',
'onCameraClosed',
'onPictureTaken',
'onVideoTakenStart',
'onVideoTakenProgress',
'onVideoTakenEnd',
'onFocusStart',
'onFocusEnd',
'onZoomChanged',
'onCameraChange',
'onOrientationChange',
'onCameraTakenError',
'onCameraError',
],
/**
* 规则如果没有配置expose则methods中的方法均对外暴露如果配置了expose则以expose的配置为准向外暴露
* ['publicMethod'] 含义为只有 `publicMethod` 在实例上可用
*/
expose: [
'open',
'reopen',
'close',
'destroyCamera',
'takePhoto',
'takePhotoSnapshot',
'takeVideo',
'takeVideoSnapshot',
'stopVideo',
'changeZoom',
'changeExposure',
'changeWhiteBalance',
'changeHdr',
'changeFacing',
'changeMode',
'changeOrientation',
'changeGrid',
'changeFlash',
'changeAudio',
'changeSuffix',
'changeSizeSelectors',
'changePreviewCorner',
'changePreviewRotation',
'changeGallery',
'openAppSettings'
],
methods: {
//
initCameraView(): UIView{
return initCameraView((success : boolean, message : string) => {
if (success) {
this.changePreviewCorner(this.previewCornerRadius, this.previewCornerRadiusRate);
this.initCamera();
} else {
console.warn(`相机初始失败或者当前设备不支持:${message}`);
}
})
},
//
initCameraPermission() {
applyPermission((allRight : boolean, grantedList : Array<String>) => {
//
if (allRight) {
this.initCameraView();
console.log(`用户同意了全部权限: ${grantedList}`);
}
// grantedList
else {
console.warn(`部分权限被拒绝: ${grantedList}`);
}
}, (doNotAskAgain : boolean, grantedList : Array<String>) => {
//
if (doNotAskAgain) {
// App
openAppSettings()
console.warn(`权限被永久拒绝: ${grantedList}`);
}
})
},
openAppSettings(){
openAppSettings()
},
//
initCamera() {
setCameraCallback((event : string, data : any) => {
switch (event) {
case "opened":
console.log("相机已打开", data)
this.$emit('onCameraOpened', data)
break
case "closed":
console.log("相机已关闭", data)
this.$emit('onCameraClosed', data)
break
case "picture":
console.log("照片", data)
this.$emit('onPictureTaken', data)
break
case "video-start":
console.log("开始录制", data)
this.$emit('onVideoTakenStart', data)
break
case "video-progress":
console.log("录制中", data)
this.$emit('onVideoTakenProgress', data)
break
case "video-end":
console.log("结束视频", data)
// this.$emit('onVideoTakenEnd', data)
break
case "video":
console.log("视频资源", data)
this.$emit('onVideoTakenEnd', data)
this.$emit('onVideoTaken', data)
break
case "focus-start":
console.log("对焦开始", data)
this.$emit('onFocusStart', data)
break
case "focus-end":
console.log("对焦结束", data)
this.$emit('onFocusEnd', data)
break
case "zoom-change":
console.log("缩放级别", data)
this.$emit('onZoomChanged', data)
break
case "camera-change":
console.log("相机设置", data)
this.$emit('onCameraChange', data)
break
case "orientation-change":
console.log("相机角度转换", data)
this.$emit('onOrientationChange', data)
break
case "take-error":
console.log("相机拍摄监测", data)
this.$emit('onCameraTakenError', data)
break
case "error":
console.log("相机出错", data)
this.$emit('onCameraError', data)
break
}
})
open();
},
//
open() {
open();
},
//
reopen() {
reopen();
},
//
close() {
close();
},
//
destroyCamera() {
if (this.$el != null) {
destroyCamera()
}
},
//
takePhoto() {
this.photoSound()
takePhoto()
},
//
takePhotoSnapshot() {
this.photoSound()
takePhotoSnapshot()
},
//
takeVideo(duration ?: Number) {
const videoDuration : Number = duration ?? 0;
this.videoSound(true)
takeVideo(videoDuration)
},
//
takeVideoSnapshot(duration ?: Number) {
const videoDuration : Number = duration ?? 0;
this.videoSound(true)
takeVideoSnapshot(videoDuration)
},
//
stopVideo() {
stopVideo();
},
// zoom
changeZoom(zoom : Number) {
setZoom(zoom)
},
// exposure
changeExposure(exposure : Number) {
setExposure(exposure)
},
//
changeWhiteBalance(whiteBalance : String) {
setWhiteBalance(whiteBalance)
},
// HDR
changeHdr(hdr : String) {
setHdr(hdr)
},
//
changeFacing(facing : String) {
setFacing(facing)
},
//
changeMode(mode : String) {
setMode(mode)
},
//
changeGrid(grid : String, color : String) {
setGrid(grid, color)
},
//
changeFlash(flash : String) {
setFlash(flash)
},
//
changeAudio(audio : String) {
setAudio(audio)
},
//
changeSuffix(suffix : String) {
setSuffix(suffix)
},
//
changeGallery(gallery : Boolean) {
setGallery(gallery)
},
// 使
changeOrientation(orientation : String) {
setOrientation(orientation)
},
//
changeSizeSelectors(width : Number, height : Number, tolerance : Number) {
setSizeSelectors(width, height, tolerance)
},
// radiusRate=0.5
changePreviewCorner(radius ?: Number, radiusRate ?: Number) {
let safeRadius : Number = 0
let safeRadiusRate : Number = 0
if (radius != null) {
safeRadius = radius
}
if (radiusRate != null) {
safeRadiusRate = radiusRate
}
setPreviewCorner(safeRadius, safeRadiusRate)
},
// 0/90/180/270
changePreviewRotation(degrees : Number) {
setPreviewRotation(degrees)
},
//
isTakingVideo() : Boolean {
return isTakingVideo();
},
//
isTakingPicture() : Boolean {
return isTakingPicture();
},
//
isOpened() : Boolean {
return isOpened();
},
//
photoSound() {
if (this.vibrate) {
vibrate(this.duration ?? 300)
}
if (this.shutter) {
shutterSound(this.sound ?? '')
}
},
//
videoSound(isStart : Boolean) {
if (this.recorder) {
recordSound(isStart, this.sound2 ?? '')
}
},
//
shortcutListener() {
//
if (!this.shortcut) {
return
}
// DONE:
// actiondown()up()
const baseOptions = {
type: "base",
intercept: true
}
keyListener(baseOptions, (action : String, code : Number, name : String) => {
console.log(`按键事件: ${action} -> ${name} (${code})`)
//
if ((code == 24 || code == 25) && action == 'up') {
this.takePhoto();
}
// ()
else if (code == 168 && action == 'down') {
this.cameraZoom = this.cameraZoom + 0.01;
if (this.cameraZoom >= 1) {
this.cameraZoom = 1
}
console.log('聚焦(加)', this.cameraZoom)
this.changeZoom(this.cameraZoom)
}
// ()
else if (code == 169 && action == 'down') {
this.cameraZoom = this.cameraZoom - 0.01;
if (this.cameraZoom <= 0) {
this.cameraZoom = 0
}
console.log('聚焦(减)', this.cameraZoom)
this.changeZoom(this.cameraZoom)
}
//
else if (code == 119 && action == 'up') {
this.cameraFacing = this.cameraFacing == 'front' ? 'back' : 'front';
console.log('设置摄像头方向', this.cameraFacing)
this.changeFacing(this.cameraFacing)
}
});
// TODO:
// actiondown()up()single()double()long()
// const fullOptions: ListenerOptions = {
// type: "full", // 使
// intercept: true //
// doubleInterval: 400, // ms 400
// longPressTime; 500, // ms 500
// }
// keyListener(fullOptions, (action : string, code : number, name : string) => {
// console.log(`: ${action} -> ${name} (${code})`)
// });
}
},
/**
* [可选实现] 组件被创建组件第一个生命周期
* 在内存中被占用的时候被调用开发者可以在这里执行一些需要提前执行的初始化逻辑
*/
created() {
//
this.initCameraPermission()
},
NVBeforeLoad() {
},
/**
* [必须实现] 创建原生View必须定义返回值类型
* 开发者需要重点实现这个函数声明原生组件被创建出来的过程以及最终生成的原生组件类型
* iOS 返回 UIView Android FrameLayout 对应
*/
NVLoad() : UIView {
// initCameraView
//
setPreviewRotation(this.previewRotation)
return this.initCameraView()
},
/**
* [可选实现] 原生View已创建
*/
NVLoaded() {
// iOS UIApplication Android UTSAndroid
},
/**
* [可选实现] 原生View布局完成
*/
NVLayouted() {
this.changePreviewCorner(this.previewCornerRadius, this.previewCornerRadiusRate);
// props
this.changePreviewRotation(this.previewRotation);
},
/**
* [可选实现] 原生View将释放
*/
NVBeforeUnload() {
},
/**
* [可选实现] 原生View已释放这里可以做释放View之后的操作
*/
NVUnloaded() {
//
if (this.$el != null) {
destroyCamera();
}
//
if (this.shortcut) {
keyDestroyer();
}
},
/**
* [可选实现] 组件销毁
*/
unmounted() {
//
if (this.$el != null) {
destroyCamera();
}
//
if (this.shortcut) {
keyDestroyer();
}
},
/**
* [可选实现] 自定组件布局尺寸用于告诉排版系统组件自身需要的宽高
* 一般情况下组件的宽高应该是由终端系统的排版引擎决定组件开发者不需要实现此函数
* 但是部分场景下组件开发者需要自己维护宽高则需要开发者重写此函数
*/
NVMeasure(size : UTSSize) : UTSSize {
return size;
}
}
</script>

View File

@ -0,0 +1,15 @@
export type iWhiteBalance = 'auto' | 'incandescent' | 'fluorescent' | 'daylight' | 'cloudy' | 'loudy'
export type iHdr = 'on' | 'off'
export type iFacing = 'front' | 'back'
export type iOrientation = 'auto' | 'portrait' | 'landscape'
export type iGrid = 'off' | 'draw_3x3' | 'draw_4x4' | 'draw_phi'
export type iFlash = 'on' | 'auto' | 'torch' | 'off'
export type iAudio = 'on' | 'off' | 'mono' | 'stereo'
export type iPhotoSuffix = 'jpeg' | 'jpg'