微信小程序二维码或者条形码扫码等功能开发中,总会遇到用户摄像头权限开启隐藏的问题,这里通常需要获取摄像头权限及时更新前端显示,最主要是用户授权提示弹窗操作后及时获取状态
问题一:用户未授权时,进入页面出现两次授权弹窗
原因是因为camera组件自动触发了一次授权,然后代码又写了wx.authorize发起授权。这里可以采取用wx:if来控制camera的加载,后续的关闭摄像头也可以用到
问题二:授权弹窗用户操作后,无法及时监听到最新状态
utils/cameraPermission.js
// utils/cameraPermission.js
/**
* 检查摄像头权限
* @returns {Promise<boolean>} 返回一个 Promise,表示是否已授权
*/
function checkCameraPermission() {
return new Promise((resolve, reject) => {
wx.getSetting({
success(res) {
resolve(!!res.authSetting['scope.camera']);
},
fail(err) {
reject(err);
}
});
});
}
/**
* 请求摄像头权限
* @returns {Promise<boolean>} 返回一个 Promise,表示是否授权成功
*/
function requestCameraPermission() {
return new Promise((resolve, reject) => {
wx.authorize({
scope: 'scope.camera',
success() {
resolve(true);
},
fail() {
resolve(false);
}
});
});
}
/**
* 检查并请求摄像头权限
* @returns {Promise<boolean>} 返回一个 Promise,表示是否已授权
*/
function checkAndRequestCameraPermission() {
return new Promise(async (resolve, reject) => {
try {
let isAuthorized = await checkCameraPermission();
if (isAuthorized) {
resolve(true);
return;
}
// 用户未授权,尝试请求授权
isAuthorized = await requestCameraPermission();
if (isAuthorized) {
resolve(true);
return;
}
// 用户拒绝授权,提示用户前往设置
wx.showModal({
title: '提示',
content: '您未授权摄像头权限,是否前往设置?',
success(modalRes) {
if (modalRes.confirm) {
wx.openSetting({
success(settingData) {
resolve(!!settingData.authSetting['scope.camera']);
},
fail(err) {
reject(err);
}
});
} else {
// 用户拒绝前往设置,不再提示
resolve(false);
}
}
});
} catch (err) {
reject(err);
}
});
}
module.exports = {
checkAndRequestCameraPermission
};
index.html
<camera wx:if="{{cameraShow}}" mode="scanCode" frame-size='large' flash="{{ cameraFlash }}" class="scan-view" bindscancode='scancode'>
<view >
<t-icon name="close" class="arrowBack" size="60rpx" />
<span>{{title}}</span>
<span style="width: 100rpx;"></span>
</view>
<view class="cameratips" wx:if="{{!hasCameraPermission}}">您未开启摄像头权限</view>
</camera>
index.js
const animation = wx.createAnimation({}); // 创建移动动画对象
// const innerAudioContext = wx.createInnerAudioContext() // 提示音
// innerAudioContext.src = ' mp3 文件网络地址 '
const { checkAndRequestCameraPermission } = require('../../../utils/cameraPermission');
Page({
/**
* 页面的初始数据
*/
data: {
canScan: true,
deviceId:'',
hasCameraPermission: false,
cameraShow:false,
cameraContext: null
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.checkAndRequestPermission();
// 监听应用显示事件
this.appShowHandler = () => {
this.checkAndRequestPermission();
};
wx.onAppShow(this.appShowHandler);
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
// 页面卸载时移除事件监听
if (this.appShowHandler) {
wx.offAppShow(this.appShowHandler);
}
// 确保在页面卸载时关闭摄像头
this.stopUsingCamera();
},
async checkAndRequestPermission() {
try {
const isAuthorized = await checkAndRequestCameraPermission();
this.setData({ hasCameraPermission: isAuthorized,cameraShow:true });
if (isAuthorized) {
this.startUsingCamera();
} else {
this.stopUsingCamera();
}
} catch (err) {
console.error('检查和请求摄像头权限时出错:', err);
}
},
startUsingCamera() {
// 创建摄像头上下文
const cameraContext = wx.createCameraContext();
this.setData({ cameraContext }, () => {
// 确保在数据更新后再打开摄像头
this.openCamera(cameraContext);
});
},
openCamera(cameraContext) {
// 这里可以添加具体的打开摄像头逻辑
console.log('尝试打开摄像头');
// 示例:拍照
cameraContext.takePhoto({
quality: 'high',
success: (res) => {
console.log('拍照成功', res.tempImagePath);
},
fail: (err) => {
console.error('拍照失败', err);
}
});
},
stopUsingCamera() {
this.setData({ cameraShow:false })
// 停止使用摄像头的逻辑
console.log('停止使用摄像头');
// 销毁摄像头上下文
if (this.data.cameraContext) {
this.data.cameraContext = null;
this.setData({ cameraShow:true })
}
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
// 添加扫描动画
startAnimation() {
let down = true
setInterval(() => {
if (down) {
animation.translateY(100).step({ duration: 3000 })
} else {
animation.translateY(0).step({ duration: 3000 })
}
down = !down
this.setData({ animation: animation.export() })
}, 3000)
},
scancode(event) {
// if (!this.data.canScan) {
// return; // 如果不能扫码,直接返回
// }
// this.setData({ canScan: false }); // 设置为不能扫码
// wx.vibrateShort() // 触发手机振动
// innerAudioContext.play() // 提示音
const { result } = event.detail // 获取校验扫描结果
this.setData({
deviceId:result
})
},
})
解释
-
checkCameraPermission:
检查用户是否已经授权摄像头权限。
-
requestCameraPermission:
请求用户授权摄像头权限,不显示自定义弹窗。如果用户授权成功,返回 true;否则返回 false。
-
checkAndRequestCameraPermission:
首先检查用户是否已经授权摄像头权限。
如果用户已授权,直接返回授权状态。
如果用户未授权,尝试请求授权(此时微信会自动弹出系统授权弹窗)。
如果用户拒绝授权,提示用户前往设置页面手动开启权限。
如果用户拒绝前往设置,不再提示。 -
onLoad:
在页面加载时检查并请求摄像头权限。
监听应用显示事件 (wx.onAppShow),在应用显示时重新检查权限。 -
onUnload:
在页面卸载时移除应用显示事件的监听。
确保在页面卸载时关闭摄像头,调用 stopUsingCamera 方法。 -
checkAndRequestPermission:
检查并请求摄像头权限。
根据权限状态更新 isCameraAuthorized 数据。
如果已授权,调用 startUsingCamera 方法;否则调用 stopUsingCamera 方法。 -
startUsingCamera:
创建摄像头上下文。
更新 cameraContext 数据,并在数据更新完成后调用 openCamera 方法尝试打开摄像头。 -
openCamera:
尝试打开摄像头并执行具体操作(例如拍照)。
处理打开摄像头的成功和失败情况。 -
stopUsingCamera:
停止使用摄像头的逻辑。
销毁摄像头上下文,确保摄像头资源被释放。
可以在这里添加其他关闭摄像头的操作。
关键点
- 权限检查: 在每次尝试打开摄像头之前,确保已经检查并获取了最新的权限状态。
- 实时监听: 使用 wx.onAppShow监听应用显示事件,在应用显示时重新检查权限。
- 关闭摄像头: 在 stopUsingCamera方法中销毁摄像头上下文,确保摄像头资源被释放。
- 确保数据更新: 在 startUsingCamera 方法中,确保在cameraContext 数据更新完成后才调用 openCamera 方法。
- 错误处理:在打开摄像头失败时,捕获并处理错误,以便进行调试和修复。
通过这种方式,确保在用户重新授权后能够正确打开摄像头,并且在权限状态发生变化时能够及时响应。同时,确保在用户拒绝授权或不再需要使用摄像头时真正关闭摄像头,释放相关资源。