微信小程序 扫码+拍照

需求背景

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

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
          })
        },
      },
    });
  },
});
相关推荐
passerma21 小时前
解决qiankun框架子应用打包后css里的图片加载404失败问题
前端·微前端·qiankun
Aliex_git21 小时前
性能优化 - Vue 日常实践优化
前端·javascript·vue.js·笔记·学习·性能优化
董世昌4121 小时前
添加、删除、替换、插入元素的全方法指南
java·开发语言·前端
qq_3168377521 小时前
Element-Plus el-table lazy 自动更新子列表
前端·vue.js·elementui
林恒smileZAZ21 小时前
Electron 的西天取经
前端·javascript·electron
这就是佬们吗21 小时前
告别 Node.js 版本冲突:NVM 安装与使用全攻略
java·linux·前端·windows·node.js·mac·web
IT_陈寒21 小时前
2024年JavaScript开发者必备的10个ES13新特性实战指南
前端·人工智能·后端
满栀58521 小时前
基于 jQuery 实现商品列表增删改查与数据统计
前端·javascript·jquery
web小白成长日记21 小时前
CSS 作用域隔离实战:React、Vue 与 Styled Components 的三种范式
前端·css·vue.js·react.js
Mr -老鬼21 小时前
Electron 与 Tauri 全方位对比指南(2026版)
前端·javascript·rust·electron·nodejs·tauri