微信小程序实现扫码并截图扫码图片转为base64的案例

250 阅读2分钟

实现功能为用户点击扫码会去扫描二维码,然后将扫描的场景图片相当于进行了一下拍照保存,然后将图片转为base64,然后将base64传给后端进行处理。目的是为了防止用户截图保存,翻拍以及拍照识别等作弊行为。因此在扫码的过程中需要拿到码信息和相关的场景图片。

页面结构

两个文件pages/index/indexpages/scan/scan分别为首页和扫码页面。

首页代码

pages/index/index.wxml代码如下

<!--pages/index/index.wxml-->
<button bind:tap="goScan">去扫码</button>
<text wx:if="{{data!=''}}">扫码结果: {{data}}</text>
<text wx:if="{{filePath}}">扫码的base64图片</text>
<image wx:if="{{filePath}}" src="{{filePath}}"></image>

page/index/index.js代码如下

Page({
    data: {
        filePath: "",
        data:""
    },
    goScan() {
        const that = this;
        wx.navigateTo({
            url: "../scan/scan",
            events: {
                acceptDataFromB: (data) => {
                    console.log("首页获取的扫码结果:", data);
                    const fs = wx.getFileSystemManager();
                    fs.readFile({
                        filePath: data.filePath,
                        encoding: 'base64', // 指定读取文件的编码为base64
                        success(res) {
                            // 下面打印的这个base64就是完整的截图的图片
                            console.log("data:image/jpeg;base64," + res.data);
                            // 上传图片...
                            that.setData({
                                filePath: "data:image/jpeg;base64," + res.data,
                                data:JSON.stringify(data)
                            })
                        },
                        fail(err) {
                            console.error("文件读取失败:", err);
                        }
                    })
                },
            },
        });
    },
});

扫码页面代码

pages/scan/scan.wxml代码如下

<camera device-position="back" flash="off" binderror="error" 
style="height: 100vh;width: 100vw;" mode="scanCode" bindscancode="onGetCode" frame-size="small"></camera>
<!-- 做一个扫码框框 -->
<view class="qrCodeKuang"></view>

pages/scan/scan.wxss代码如下

/* subF/pages/scanCode/scanCode.wxss */
.qrCodeKuang {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 500rpx;
    height: 500rpx;
    background: linear-gradient(#ffffff, #ffffff) left top,
        linear-gradient(#ffffff, #ffffff) left top,
        linear-gradient(#ffffff, #ffffff) right top,
        linear-gradient(#ffffff, #ffffff) right top,
        linear-gradient(#ffffff, #ffffff) right bottom,
        linear-gradient(#ffffff, #ffffff) right bottom,
        linear-gradient(#ffffff, #ffffff) left bottom,
        linear-gradient(#ffffff, #ffffff) left bottom;
    background-repeat: no-repeat;
    background-size: 6rpx 60rpx, 60rpx 6rpx;
}

pages/scan/scan.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);
      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;
        },
      });
    },
  });