微信小程序 扫码+拍照

需求背景

在扫二维码的时候获取到结果并且拍照上传

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
          })
        },
      },
    });
  },
});
相关推荐
啃火龙果的兔子3 分钟前
前端单元测试覆盖率工具有哪些,分别有什么优缺点
前端·单元测试
「、皓子~30 分钟前
后台管理系统的诞生 - 利用AI 1天完成整个后台管理系统的微服务后端+前端
前端·人工智能·微服务·小程序·go·ai编程·ai写作
就改了33 分钟前
Ajax——在OA系统提升性能的局部刷新
前端·javascript·ajax
凌冰_35 分钟前
Ajax 入门
前端·javascript·ajax
京东零售技术1 小时前
京东小程序JS API仓颉改造实践
前端
老A技术联盟1 小时前
从小白入门,基于Cursor开发一个前端小程序之Cursor 编程实践与案例分析
前端·小程序
风铃喵游1 小时前
构建引擎: 打造小程序编译器
前端·小程序·架构
sunbyte1 小时前
50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | ThemeClock(主题时钟)
前端·javascript·css·vue.js·前端框架·tailwindcss
小飞悟1 小时前
🎯 什么是模块化?CommonJS 和 ES6 Modules 到底有什么区别?小白也能看懂
前端·javascript·设计
浏览器API调用工程师_Taylor1 小时前
AOP魔法:一招实现登录弹窗的全局拦截与动态处理
前端·javascript·vue.js