findme-miniprogram-frontend/subpackages/qr/qr-scan/qr-scan.js
2025-12-27 17:16:03 +08:00

262 lines
8.3 KiB
JavaScript
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.

import jsQR from '../utils/jsQR';
Page({
data: {
// 相机相关配置
cameraContext: null,
devicePosition: 'back', // 后置摄像头, 'front' : 'back';
flashMode: 'off', // 闪光灯默认关闭,'off' : 'torch'
// 扫码框相关配置
scanBoxWidth: 580, // 扫码框宽度rpx
scanBoxHeight: 580, // 扫码框高度rpx
scanLineTop: 0, // 扫描线初始位置
scanLineSpeed: 2, // 扫描线移动速度
// 屏幕适配相关
windowWidth: 375, // 屏幕宽度rpx
windowHeight: 667, // 屏幕高度rpx
scanBoxLeft: 0, // 扫码框左侧偏移
scanBoxTop: 0, // 扫码框顶部偏移
isScanning: false, // 扫描状态标记,防止重复扫描
},
// 页面每次显示时(包括跳转返回后)触发
onShow() {
// 强制重置扫描状态,避免返回后无法扫码
this.setData({
isScanning: false
});
this.scanLock = false; // 同步释放全局锁
},
onLoad(options) {
const sis = wx.getWindowInfo();
// 初始化相机上下文
this.setData({
cameraContext: wx.createCameraContext(this),
// 获取屏幕尺寸用于适配
windowWidth: 750,
windowHeight: sis.windowHeight / sis.windowWidth * 750,
}, () => {
// 计算扫码框位置(居中显示)
this.calcScanBoxPosition();
// 启动扫描线动画
this.startScanLineAnimation();
});
},
// 计算扫码框居中位置
calcScanBoxPosition() {
const { windowWidth, windowHeight, scanBoxWidth, scanBoxHeight } = this.data;
const scanBoxLeft = (windowWidth - scanBoxWidth) / 2;
const scanBoxTop = (windowHeight - scanBoxHeight) / 2 - 100; // 向上偏移100rpx
this.setData({ scanBoxLeft, scanBoxTop });
},
// 扫描线动画
startScanLineAnimation() {
const { scanBoxHeight, scanLineSpeed } = this.data;
let scanLineTop = 0;
this.animationInterval = setInterval(() => {
scanLineTop += scanLineSpeed;
if (scanLineTop >= scanBoxHeight) {
scanLineTop = 0;
}
this.setData({ scanLineTop });
}, 30);
},
// 打开/关闭闪光灯
toggleFlash() {
this.setData({
flashMode: this.data.flashMode === 'torch' ? 'off' : 'torch',
});
},
/**
* 扫码结果
* @param {*} e
*/
handleScanCode(e) {
// 记录当前时间戳
const now = Date.now();
// 限制 2 秒内只能处理一次扫码
if (this.lastScanTime && now - this.lastScanTime < 2000) {
return;
}
this.lastScanTime = now;
// 双重锁机制确保不会重复处理
if (this.data.isScanning || this.scanLock) {
return;
}
this.setData({ isScanning: true });
this.scanLock = true;
console.log("扫码结果123-------", e);
// 处理扫码逻辑
this.triggerEvent('scancode', e.detail);
this.onCode(e.detail.result)
},
/**
* 点击跳转到我的二维码页面
* @param {*} e
*/
goCode(e) {
wx.showToast({ title: "跳转到我的二维码页面" });
wx.reLaunch({
url: '/subpackages/qr/qr-code/qr-code',
})
},
/**
* 手动选择照片后扫码
*/
chooseImageFirst() {
const that = this;
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['album'],
maxDuration: 30,
camera: 'back',
fail(){
wx.showToast({ title: "请选择二维码" });
},
success(res) {
const src = res.tempFiles[0].tempFilePath
that.setData({
imgPath :src
})
wx.showLoading({
title: '识别中...',
})
console.log("图片地址",src);
wx.getImageInfo({
src: src,
success(res) {
console.log("图片信息",res);
// 转Canvas像素数据
const canvas = wx.createOffscreenCanvas({ type: '2d', width: res.width, height: res.height });
const canvasCtx = canvas.getContext('2d');
const img = canvas.createImage();
img.onload = () => {
canvasCtx.drawImage(img, 0, 0, res.width, res.height);
const imageData = canvasCtx.getImageData(0, 0, res.width, res.height);
// jsQR识别
const code = jsQR(imageData.data, res.width, res.height);
console.log('识别结果', code);
if(code){
wx.hideLoading()
that.onCode(code.data)
}else{
wx.showToast({ title: "未识别到二维码" });
}
};
img.src = src;
}
})
}
})
},
/**
* 扫码后获得的二维码内容
* @param {*} data
*/
onCode(data){
try {
console.log("扫码结果:", data);
if(!data || !data.startsWith('FINDME:')){
wx.showToast({
title: '无效的二维码这不是FindMe的用户二维码',
icon:"none"
})
setTimeout(()=>this.setData({imgPath : "" })
,1500)
this.setData({isScanning:false});
this.scanLock = false; // 同步释放全局锁
return
}
const customId = data.split(':')[1]
console.log('dataJson',customId);
this.handleCode(customId)
} catch (error) {
wx.showToast({ title: "无法解析二维码内容"});
console.log("无法解析二维码内容",error);
}finally{
this.setData({isScanning:false});
this.scanLock = false; // 同步释放全局锁
}
},
async handleCode(customId){
let userInfo = wx.getStorageSync('userInfo');
if(customId == userInfo.customId){
wx.showToast({
title: '不能添加自己为好友',
icon:'none'
})
setTimeout(()=>this.setData({imgPath : "" })
,1500)
this.setData({isScanning:false});
this.scanLock = false; // 同步释放全局锁
return
}
// 检查是否是好友关系
await this.checkFriendRelationFromScan(customId);
},
// 检查好友关系(从扫码调用)
async checkFriendRelationFromScan(customId) {
const friendAPI = require('../../../utils/friend-api.js');
wx.showLoading({ title: '加载中...', mask: true });
const friendDetailResponse = await friendAPI.getFriendDetail(customId).catch(() => null);
wx.hideLoading();
const isFriend = friendDetailResponse?.code === 0 && friendDetailResponse?.data;
this.navigateToUserDetail(customId, isFriend);
},
// 跳转到用户详情页面
navigateToUserDetail(customId, isFriend = false) {
const url = isFriend
? `/subpackages/social/friend-detail/friend-detail?customId=${customId}`
: `/subpackages/social/user-preview/user-preview?customId=${customId}`;
wx.navigateTo({
url: url,
success: () => {
this.setData({ imgPath: "" });
},
fail: (err) => {
console.error('跳转失败:', err);
wx.showToast({ title: '跳转失败,请重试', icon: 'none' });
this.setData({ imgPath: "",isScanning: false});
this.scanLock = false; // 同步释放全局锁
}
});
},
onUnload() {
// 页面卸载时停止扫码和动画
if (this.animationInterval) {
clearInterval(this.animationInterval);
}
},
// 授权回调
handleGetUserInfo(e) {
if (e.detail.userInfo) {
// 授权成功后重新初始化
this.onLoad();
} else {
wx.showToast({ title: '需要授权相机权限才能使用扫码功能', icon: 'none' });
}
}
});