微信小程序 扫码+拍照

需求背景

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

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
          })
        },
      },
    });
  },
});
相关推荐
qq_386322693 小时前
华为网路设备学习-21 IGP路由专题-路由过滤(filter-policy)
前端·网络·学习
蓝婷儿8 小时前
前端面试每日三题 - Day 32
前端·面试·职场和发展
星空寻流年9 小时前
CSS3(BFC)
前端·microsoft·css3
九月TTS9 小时前
开源分享:TTS-Web-Vue系列:Vue3实现固定顶部与吸顶模式组件
前端·vue.js·开源
CodeCraft Studio9 小时前
数据透视表控件DHTMLX Pivot v2.1发布,新增HTML 模板、增强样式等多个功能
前端·javascript·ui·甘特图
一把年纪学编程9 小时前
【牛马技巧】word统计每一段的字数接近“字数统计”
前端·数据库·word
llc的足迹10 小时前
el-menu 折叠后小箭头不会消失
前端·javascript·vue.js
九月TTS10 小时前
TTS-Web-Vue系列:移动端侧边栏与响应式布局深度优化
前端·javascript·vue.js
Johnstons10 小时前
AnaTraf:深度解析网络性能分析(NPM)
前端·网络·安全·web安全·npm·网络流量监控·网络流量分析
whatever who cares11 小时前
CSS3 伪元素(Pseudo-elements)大全
前端·css·css3