daShangDao_scanBook/uni_modules/ima-camera-view/utssdk/app-android/index.vue

652 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>