feat:单拍/连拍按钮改名,正方形预览+拍照,按钮放大,完成与翻转调换
This commit is contained in:
parent
085855e39e
commit
834380ce5e
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
438
uni_modules/ima-camera-view/changelog.md
Normal file
438
uni_modules/ima-camera-view/changelog.md
Normal file
@ -0,0 +1,438 @@
|
||||
## 1.2.2(2026-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.1(2026-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.0(2026-05-24)
|
||||
> 新增功能
|
||||
|
||||
- 无
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 无
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 无
|
||||
|
||||
> 由于 **`harmony端`** 不支持兼容性组件,`harmony端`(指的是`harmony 5.0+`)不再本组件中更新,插件正在更新中
|
||||
## 1.1.16(2026-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.15(2026-02-02)
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 无
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 1、部分手机无法录像的问题或者录像中没有返回结果的问题(可能解决`{ "errorCode" => 5, "reason" => "录像失败", "message" => "java.lang.RuntimeException: start failed." }`的问题)
|
||||
- 1.1、增加了待录像(切换 VIDEO 模式后延后执行)
|
||||
- 1.2、待拍照(切换 PICTURE 模式后延后执行)
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 1、增加拍照、录像、结束录像时异常抛出
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
|
||||
## 1.1.14(2026-01-30)
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 无
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 1、部分手机无法录像的问题或者录像中没有返回结果的问题
|
||||
- 2、修改代码结构,准备改成兼容组件模式,为`Harmony Next`做准备
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 无
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
## 1.1.13(2026-01-22)
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 无
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 1、修复获取Activity实例、Application上下文上下文出现的问题!
|
||||
- 2、修复相机按键、蓝牙自拍杆监听事件的问题
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 1、相机震动做了兼容处理
|
||||
- 2、相机的权限申请做了兼容处理
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
|
||||
## 1.1.12(2026-01-19)
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 无
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 1、修复安卓8.1事打开相机报错:“相机初始失败或者当前设备不支持:创建失败:When targetSdkVersion >= 33
|
||||
should use amdroid.permission.xxx,...”
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 无
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
## 1.1.11(2026-01-07)
|
||||
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 1、增加`take-error(相机拍摄监测)`、`orientation-change(相机角度转换)`、、`camera-change(相机设置监听)`
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 1、在`setSizeSelectors`方法增加错误回调,便于排查一些手机调取无反应的问题
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 无
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
|
||||
## 1.1.10(2026-01-07)
|
||||
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 1、增加`take-error(相机拍摄监测)`、`orientation-change(相机角度转换)`、、`camera-change(相机设置监听)`
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 1、在`setSizeSelectors`方法增加错误回调,便于排查一些手机调取无反应的问题
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 无
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
|
||||
## 1.1.9(2026-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.8(2026-01-05)
|
||||
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 1、是否将拍摄文件保存到本地可见媒体(即相册)的方法`changeGallery`
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 1、相机权限的问题,如用户拒绝后可直接跳转到当前 App 的系统设置页
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 无
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
|
||||
## 1.1.7(2026-01-04)
|
||||
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 无
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 1.1.6 版本推包后上传不成功的问题(下载和导入还是1.1.5的代码)
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 无
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
|
||||
## 1.1.6(2026-01-04)
|
||||
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 无
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 1、安卓高版本(安卓16)出现拍照的问题
|
||||
- 2、在一些其他终端设备(非手机)中出现相机预览画面与实际不符合的问题`【待复测】`
|
||||
- 3、一些配置不生效的问题,如changeAspectRatio、changeOrientation(由于相机加载顺序问题导致的)
|
||||
- 4、一些其他终端设备(非手机)进入卡顿或者黑屏几秒才显示相机预览页面`【待复测】`
|
||||
- 5、一些手机设备由于权限问题导致第一次进入是黑屏或者后几次进入偶尔出现黑屏闪现的问题`【待复测】`
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 1、将之前的uts改成了kotlin的写法
|
||||
- 2、将组件是的抛出调整成`CameraManager.setCameraCallback`,方便后期增加参数输出、控制
|
||||
- 3、优化了相机加载的顺序问题、以及设置相机时的问题
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
|
||||
## 1.1.5(2025-12-29)
|
||||
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 新增设置相机使用设备方向的方法`changeOrientation`
|
||||
- 新增设置相机网格及颜色的方法`changeGrid`
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 无
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 无
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
|
||||
- 完善鸿蒙`Harmony`版本的并进行更新(已完成开发,测试中)
|
||||
|
||||
## 1.1.4(2025-12-28)
|
||||
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 新增设置曝光值的方法`changeExposure`
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 修复动态权限申请的问题(即Android 12 及以下、Android 13+)
|
||||
- 修复 Android 16(即API级别为36)时摄像回调的问题
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 无
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
|
||||
- 完善鸿蒙`Harmony`版本的并进行更新
|
||||
|
||||
## 1.1.3(2025-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.2(2025-11-06)
|
||||
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 无
|
||||
|
||||
> 问题修复
|
||||
|
||||
- `快捷键拍照(蓝牙自拍杆、手机音量键)`拍照时,返回后按钮无法操作的问题修复
|
||||
- 页面在没启用`shortcut(快捷键拍照)`时,也会进入快捷键的问题修复
|
||||
- 修复在低版本`HubuildX`时打包出现找不到类型的问题
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 优化了相机资源在进入时卡顿的问题
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
|
||||
- 完善鸿蒙`Harmony`版本的并进行更新
|
||||
|
||||
## 1.1.1(2025-11-03)
|
||||
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 新增设置照片输出格式的方法`changeSuffix`
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 修复照片格式无法设置的问题【原因是在uni给出的生命周期内,无法取到`props`的参数,只能在`watch`中处理】
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 无
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
|
||||
- 完善鸿蒙`Harmony`版本的并进行更新
|
||||
|
||||
## 1.1.0(2025-10-31)
|
||||
|
||||
> 新增功能
|
||||
|
||||
- 快捷键拍照: 如:按两下音量键拍照、按音量键拍照等等(可自定义)
|
||||
- 蓝牙自拍杆: 提供可以连接蓝牙自拍杆拍照、对焦等(可自定义)
|
||||
- 相机`是否开启蓝牙自拍杆、手机快捷键拍照`,可自定义快捷键,具体参考参数`shortcut`
|
||||
-
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 无
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 无
|
||||
|
||||
> 计划新增功能(大版本更新【即 `1.2.0` 开始】)
|
||||
|
||||
- 完善鸿蒙`Harmony`版本的并进行更新
|
||||
|
||||
## 1.0.5(2025-10-30)
|
||||
|
||||
> 新增功能(临时更新)
|
||||
|
||||
- 相机`录像`方法增加设置`视频录制时长限制`
|
||||
- 相机`录像`声音默认系统录像声音,也可自定义,具体参考参数`recorder`、`sound2`
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 无
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 无
|
||||
|
||||
## 1.0.4(2025-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.3(2025-10-16)
|
||||
|
||||
> 新增功能
|
||||
|
||||
- 增加拍照声音,可以自定义声音文件(默认手机原相机声音)
|
||||
- 增加拍照震动,可以自定义震动时长(默认200毫秒)
|
||||
|
||||
> 问题修复
|
||||
> 无
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 照片文件的分辨率取手机原相机的,提高照片的清晰度
|
||||
|
||||
## 1.0.2(2025-09-09)
|
||||
|
||||
> 新增功能
|
||||
> 无
|
||||
|
||||
> 问题修复
|
||||
> 无
|
||||
|
||||
> 功能优化
|
||||
|
||||
- 支持拍照预览或从相册选择后,返回当前相机页面时,如果出现黑屏状态,可以重新自己手动拉起相机
|
||||
|
||||
## 1.0.1(2025-09-05)
|
||||
|
||||
> 新增功能
|
||||
> 无
|
||||
|
||||
> 问题修复
|
||||
|
||||
- 修复高版本(`4.66`时)打包报错:`*(项目路径)*//index.kt:44:12 Unresolved reference: _uA`、
|
||||
`*(项目路径)*/index.kt:118:48 Unresolved reference: _uO`的问题
|
||||
- 修复前置拍出来的图片是镜像的问题
|
||||
- 修复照片拍出来尺寸不对、照片过小的问题
|
||||
|
||||
> 功能优化
|
||||
> 无
|
||||
|
||||
## 1.0.0(2025-05-27)
|
||||
|
||||
> 新增功能
|
||||
|
||||
- 新增方法:`close`、 `open`、 `takePhoto`、 `takePhotoSnapshot`、 `takeVideo`、`takeVideoSnapshot`、
|
||||
`stopVideo`、 `changeZoom`、 `changeFacing`、`changeFlash`、`changeAudio`
|
||||
- 新增事件:`onPictureTaken`、 `onVideoTakenStart`、 `onVideoTakenEnd`、 `onFocusStart`、 `onFocusEnd`
|
||||
- `android端`的所有功能已完成开发、测试
|
||||
- `harmony端`计划开发中
|
||||
- 初始版
|
||||
|
||||
> 问题修复
|
||||
> 无
|
||||
|
||||
> 功能优化
|
||||
> 无
|
||||
BIN
uni_modules/ima-camera-view/encrypt
Normal file
BIN
uni_modules/ima-camera-view/encrypt
Normal file
Binary file not shown.
126
uni_modules/ima-camera-view/package.json
Normal file
126
uni_modules/ima-camera-view/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
373
uni_modules/ima-camera-view/readme.md
Normal file
373
uni_modules/ima-camera-view/readme.md
Normal 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。
|
||||
|
||||
## 常见的比例的定义(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` |
|
||||
|
||||
@ -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>
|
||||
BIN
uni_modules/ima-camera-view/utssdk/app-android/ImaCamera.kt
Normal file
BIN
uni_modules/ima-camera-view/utssdk/app-android/ImaCamera.kt
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
uni_modules/ima-camera-view/utssdk/app-android/ImaCameraUtils.kt
Normal file
BIN
uni_modules/ima-camera-view/utssdk/app-android/ImaCameraUtils.kt
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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' }"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
uni_modules/ima-camera-view/utssdk/app-android/index.uts
Normal file
BIN
uni_modules/ima-camera-view/utssdk/app-android/index.uts
Normal file
Binary file not shown.
651
uni_modules/ima-camera-view/utssdk/app-android/index.vue
Normal file
651
uni_modules/ima-camera-view/utssdk/app-android/index.vue
Normal 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:按行业标准
|
||||
// 基础版(action只有:down(按下)、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:这个需要自己实现
|
||||
// 高阶版(action有:down(按下)、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>
|
||||
Binary file not shown.
14
uni_modules/ima-camera-view/utssdk/app-ios/Info.plist
Normal file
14
uni_modules/ima-camera-view/utssdk/app-ios/Info.plist
Normal 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>
|
||||
10
uni_modules/ima-camera-view/utssdk/app-ios/config.json
Normal file
10
uni_modules/ima-camera-view/utssdk/app-ios/config.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"deploymentTarget": "12.0",
|
||||
"frameworks": [
|
||||
"AVFoundation",
|
||||
"Photos",
|
||||
"UIKit",
|
||||
"Foundation",
|
||||
"AudioToolbox"
|
||||
]
|
||||
}
|
||||
BIN
uni_modules/ima-camera-view/utssdk/app-ios/index.uts
Normal file
BIN
uni_modules/ima-camera-view/utssdk/app-ios/index.uts
Normal file
Binary file not shown.
634
uni_modules/ima-camera-view/utssdk/app-ios/index.vue
Normal file
634
uni_modules/ima-camera-view/utssdk/app-ios/index.vue
Normal 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:按行业标准
|
||||
// 基础版(action只有:down(按下)、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:这个需要自己实现
|
||||
// 高阶版(action有:down(按下)、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>
|
||||
15
uni_modules/ima-camera-view/utssdk/interface.uts
Normal file
15
uni_modules/ima-camera-view/utssdk/interface.uts
Normal 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'
|
||||
Loading…
Reference in New Issue
Block a user