微信小程序 扫码+拍照

需求背景

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

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
          })
        },
      },
    });
  },
});
相关推荐
喜欢踢足球的老罗11 小时前
一张跨域图的“四次换乘“:blob URL 与 Chrome 扩展架构里的工程艺术
前端·chrome·架构
程序员黑豆11 小时前
AI全栈开发 - Java:基本数据类型 vs 引用数据类型的内存存储
java·前端·ai编程
FserSuN11 小时前
Chrome CORS / PNA / LNA 问题排查与解决方案
前端·chrome
小小小小宇11 小时前
Claude Code 自动运行方法大全
前端
道友可好11 小时前
AI 测试全绿,代码却是错的
前端·人工智能·后端
国科安芯12 小时前
商业航天通信载荷数字处理单元供电架构研究——基于ASP7A84AS的高精度低压差线性稳压器技术分析
前端·单片机·嵌入式硬件·fpga开发·架构·安全性测试
TangentDomain12 小时前
AI 写代码时代,游戏 UI 架构为什么停在 MVP?
前端·游戏·架构
英勇无比的消炎药12 小时前
前端提效神器全新AI组件库TinyRobot改写日常开发模式
前端·vue.js
GuWenyue12 小时前
10分钟搞定TodoList实战!从0搭建Bun+TS的RESTful接口服务
前端·typescript·bun