需求背景
在扫二维码的时候获取到结果并且拍照上传
wx.scanCode
微信小程序有提供扫码API,但是只能获取到结果,无法获取到图片
camera组件

camera组件提供了两种模式,相机、扫码模式。初步设想的方法是用扫码模式去扫码,在扫码获取到结果触发bindscancode
回调的时候再去调用上下文对象去拍照片

js
Page({
// 拍照
takePhoto() {
const ctx = wx.createCameraContext();
ctx.takePhoto({
quality: "low",
success: (res) => {
console.log(res.tempImagePath);
},
fail: (err) => {
console.log("拍照失败", err);
},
});
},
// 获取到扫码结果
onGetCode(e) {
console.log("获取到扫码结果", e.detail.result);
this.takePhoto();
},
});
实测现在无法支持扫码模式下拍照{errMsg: "Not allow to invoke takePhoto in 'scanCode' mode."}
,只能另做打算

接下来试了前端来获取帧数据来解码,可以实现,但是准确度不高,效率不行。
最终方案
camera
扫码模式。获取到码结果之后立即获取当前帧并上传。
html
<!--pages/scan/index.wxml-->
<camera device-position="back" flash="off" binderror="error"
style="height: 100vh;width: 100vw;" mode="scanCode" bindscancode="onGetCode" frame-size="small"></camera>
js
Page({
onLoad() {
// 帧数组
this.frameList = [];
this.isEnd = false;
// 相机上下文对象
this.cameraContext = wx.createCameraContext();
this.cameraListener = this.cameraContext.onCameraFrame((frame) => {
// 存储一张帧图片
if (this.frameList.length === 0) {
this.frameList.push({
data: frame.data,
width: frame.width,
height: frame.height,
});
this.stopGetFrame();
}
});
},
// 获取到扫码结果
onGetCode(e) {
console.log("获取到扫码结果", e.detail.result);
this.qrCodeResult = e.detail.result;
if (this.frameList.length === 0 && !this.isEnd) this.startGetFrame();
},
// 开始获取帧图片
startGetFrame() {
this.cameraListener.start();
},
// 停止监听帧数据
stopGetFrame() {
this.cameraListener.stop();
this.transferFrame();
},
// frame转base64
frameToBase64(frameInfo) {
const frameWidth = frameInfo.width;
const frameHeight = frameInfo.height;
return new Promise((resolve, reject) => {
if (!frameInfo) {
reject(new Error("No frame data available"));
return;
}
// 创建离屏 canvas
const offscreenCanvas = wx.createOffscreenCanvas({
type: "2d",
width: frameWidth,
height: frameHeight,
});
const ctx = offscreenCanvas.getContext("2d");
// 创建 ImageData 对象
const imgData = ctx.createImageData(frameWidth, frameHeight);
// 将 ArrayBuffer 数据复制到 ImageData
const uint8Array = new Uint8Array(frameInfo.data);
imgData.data.set(uint8Array);
// 将 ImageData 绘制到 canvas
ctx.putImageData(imgData, 0, 0);
// 将 canvas 内容转换为 base64
const base64 = offscreenCanvas.toDataURL("image/png");
resolve(base64);
});
},
// 去掉前缀
removeBase64Prefix(base64Data) {
return base64Data.replace(/^data:image\/[a-z]+;base64,/, "");
},
// 处理帧数据
async transferFrame() {
console.log("获取到帧数据", this.frameList);
const fs = wx.getFileSystemManager();
let filePath = `${wx.env.USER_DATA_PATH}/example.png`;
const base64 = this.removeBase64Prefix(
await this.frameToBase64(this.frameList[0])
);
fs.writeFile({
filePath,
encoding: "base64",
data: base64,
success: (res) => {
console.log("写入成功", res);
const eventChannel = this.getOpenerEventChannel();
eventChannel.emit("acceptDataFromB", {
data: this.qrCodeResult,
filePath,
});
this.frameList = [];
this.frameList.length = 0;
this.isEnd = true;
// 返回页面再上传文件
wx.navigateBack();
},
fail(err) {
console.error("写入失败", err);
this.frameList = [];
this.frameList.length = 0;
},
});
},
});
html
<!--pages/index/index.wxml-->
<button bind:tap="goScan">去扫码</button>
<text>扫码结果</text>
<image wx:if="{{filePath}}" src="{{filePath}}"></image>
js
Page({
data: {
filePath: "",
},
goScan() {
wx.navigateTo({
url: "../scan/index",
events: {
acceptDataFromB: (data) => {
console.log("获取到的扫码结果:", data);
// 上传图片...
this.setData({
filePath:data.filePath
})
},
},
});
},
});