小程序canvas生成二维码图片踩的坑

时间:2024-02-25 10:27:26

1:生成临时图片,保证画布被加载以及渲染(即本身不可以 hidden 或是 上级元素不可以 hidden 或是 wx:if 隐藏等)

 == 》 建议:因为 canvas 的组件层级(z-index)是最高的,无法通过层级改变,如自定义的弹框类似的组件总会被挡住 

 == 》 若不想给挡住,便要控制 canvas 隐藏(hidden 、 wx:if)

          但是利用 API ctx.setGlobalAlpha = 0(只是变成不透明,视觉上给隐藏,但是还是还在,若是那位置有 tap 等事件就尴尬了)

== 》 因为我只是利用 canvas生成二维码图片, swiper 轮播等使用,必须要二维码图片。

 

+++++ 诸多使用把canvas生成的二维码生成图片原因原因

1:我自定义使用 alert 、confirm、message组件,因为canvas 组件由客户端native生成,改变不了层级,

      所以总是总之 隐藏好麻烦,也解决不了我使用轮播使用二维码 

2:官网(CSS动画对canvas无效)

  •  tipcanvas 组件是由客户端创建的原生组件,它的层级是最高的,不能通过 z-index 控制层级。
  •  tip: 请勿在 scroll-viewswiperpicker-viewmovable-view 中使用 canvas 组件。
  •  tipcss 动画对 canvas 组件无效。
  •  bug: 避免设置过大的宽高,在安卓下会有crash的问题

 

解决的方法:

a:把 需要的 canvas 单独显示在屏幕外的十万八千里之外(position:fixed;top:-1000px;left:-1000px)

=》单独 单独 单独 啊 不要有父级元素(反正是用生成图片用)

注意了:保证 canvas 的 宽高与 生成二维码的宽高参数保持一致

1  <canvas canvas-id="QRCode-canvas-0" style="width:{{SIZE}}px;height:{{SIZE}}px;position: fixed;left: -500px; bottom: -500px;"></canvas>

b:利用插件(网上很多说明 jquery-qrcode.js插件)自己参考修改成 wx 的接口的;如 使用 wepy框架  ES6语法

import {
    QRCode,
    QRErrorCorrectLevel
} from \'./qrcode\';
import wepy from \'wepy\';
export default class QRCodeMixin extends wepy.mixin {

    data = {
        //设置画布大小
        SIZE: 0,

    };
    async setSize(customSize = 150) {
        const res = await wepy.getSystemInfoSync();
        console.log(\'获取系统信息:*************** \', res)
            //不同屏幕下canvas的适配比例;设计稿是750宽
            // iphone6 => 180 px  deviceWidth 375px
        const scale = 375 / customSize;
        const width = res.windowWidth / scale;
        return parseInt(width);
    };
    async drawQRCode(ops = {}) {
        console.time(\'+++++++++++ 画布绘制总耗时:\');
        const DEFAULT = {
            render: \'canvas\', //设置渲染方式 (有两种方式 table和canvas,默认是canvas)
            typeNumber: 6, //计算模式    
            background: \'#ffffff\', //背景颜色   
            foreground: \'#1A1A1A\', //前景颜色 
            correctLevel: QRErrorCorrectLevel.H, //纠错等级  QRErrorCorrectLevel.H, =》 L 、M 、Q 、R、 H
            canvasId: \'QRCode-canvas\', //canvas对象 id 
            text: \'QR-Code\' //生成二维码的内容
        };
        let options = Object.assign({}, DEFAULT, {
            width: ops.size,//保持输入的宽高与canvas组件的宽高一致
            height: ops.size
        }, ops);

        if (!options.canvasId || options.canvasId == \'\') {
            throw new Error(\'请输入有效的画布 id !\')
            return false;
        }
        const createCanvas = async(options) => {
            // create the qrcode itself
            const qrcode = new QRCode(options.typeNumber, options.correctLevel);
            qrcode.addData(options.text);
            qrcode.make();
            // get canvas context
            const ctx = wx.createCanvasContext(options.canvasId);
            console.log(\'++++++++++++++ 当前画布对象:\', ctx);
            // compute tileW/tileH based on options.width/options.height
            const tileW = options.width / qrcode.getModuleCount();
            const tileH = options.height / qrcode.getModuleCount();
            //保存当前的绘图上下文
            ctx.save();
            // draw in the canvas
            for (let row = 0; row < qrcode.getModuleCount(); row++) {
                for (let col = 0; col < qrcode.getModuleCount(); col++) {
                    let style = qrcode.isDark(row, col) ? options.foreground : options.background;
                    ctx.setFillStyle(style);
                    let w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW));
                    let h = (Math.ceil((row + 1) * tileW) - Math.floor(row * tileW));
                    ctx.fillRect(Math.round(col * tileW), Math.round(row * tileH), w, h);
                }
            };
            //恢复之前保存的绘图上下文。
            ctx.restore();
            ctx.draw(true, () => {           
                //一定要延时一下,总是不延时,我本人的手机生成的二维码还是图片有bug
              了
                setTimeout(() => {
                    wepy.canvasToTempFilePath({
                        canvasId: options.canvasId,
                        quality: 0.9,
                        x: 0,
                        y: 0,
                        width: options.width,
                        height: options.height,
                    }).then(res => {
                        options.callback && options.callback(res.tempFilePath);
                        console.log(\'++++++++++++++++  wx.canvasToTempFilePath:\', res);
                    });
                }, 700);
            );
            });
        };
        createCanvas(options);
    };
};

  生成临时图片关键的一步:

 1  ctx.draw(true, () => {
 2                 setTimeout(() => {
 3                     wepy.canvasToTempFilePath({
 4                         canvasId: options.canvasId,
 5                         quality: 0.9,
 6                         x: 0,
 7                         y: 0,
 8                         width: options.width,
 9                         height: options.height,
10                     }).then(res => {
11                         options.callback && options.callback(res.tempFilePath);
12                         console.log(\'++++++++++++++++  wx.canvasToTempFilePath:\', res);
13 
14                     });
15                 }, 700);
16 
17             });

不懂参考官网 API 说明:https://developers.weixin.qq.com/miniprogram/dev/api/

 

把混合的模块加载对应的Page 上,使用

//把二维码生成图片
    const self = this;
this.drawQRCode({ text: ticketNo, size, callback(tempFilePath) { //把二维码生成图片 self.QRCodeFilePath = tempFilePath; self.$apply(); } });
//
tempFilePath 就是上面 QRCodeMixin 封装好的生成的临时图片,再用 data 接收

 

 生成的图片:

坑1:刚开始经常遇到 andriad 机器生成图片白色的什么内容也没有(ios就正常) (因为没有在 wx.draw 后的回调调用)

坑2:控制台打印:canvas 为空 =》 就是上级或是本身给隐藏了(hidden 或是 wx:if)

坑3:生成的临时图片有时候正常有时候不正常(开发工具都正常的),真机就遇到 ,后来在 延时一下再执行 wepy.canvasToTempFilePath

坑4:轮播图本来用一个 canvas 画图并导出图片,可是有时候有bug的,有些二维码不正常的(所以我是用了三个 canvas 组件,递归执行完,很完美的图片出来,虽然用了三个看起来臃肿,本来使用 swier 触发再对应渲染的内容后再生成图片的)

 <canvas canvas-id="QRCode-canvas-0" style="width:{{SIZE}}px;height:{{SIZE}}px;position: fixed;left: -500px; bottom: -500px;"></canvas>
 <canvas canvas-id="QRCode-canvas-1" style="width:{{SIZE}}px;height:{{SIZE}}px;position: fixed;left: -50px; bottom: -1000px;"></canvas>
 <canvas canvas-id="QRCode-canvas-2" style="width:{{SIZE}}px;height:{{SIZE}}px;position: fixed;left: -50px; bottom: -1500px;"></canvas>

生成图片

   async draw(indexValidTicketList) {
      const self = this;
      //把二维码生成图片
      self.drawQRCode({
        canvasId: \'QRCode-canvas-\' + self.DRAW_QRCODE_INDEX,
        text: indexValidTicketList[self.DRAW_QRCODE_INDEX].ticketNo,
        size: self.SIZE,
        callback(tempFilePath) {
          //把二维码生成图片
          indexValidTicketList[self.DRAW_QRCODE_INDEX][\'QRCodeFilePath\'] = tempFilePath;
          self.$apply();
          //
          self.DRAW_QRCODE_INDEX++;
          //最多显示三张
          if (self.DRAW_QRCODE_INDEX < indexValidTicketList.length) {
            self.draw(indexValidTicketList);
          } else {
            self.DRAW_QRCODE_INDEX = 0;
            self.$apply();
          }
        }
      });
    };

 

能成功生成一张图片,至于多张图片你们自己觉得合理的方法去实现,这次 用 canvas 的坑,你看到这里,这些坑你也在踩,刚好我踩过了,给你们参考,希望对你们有帮助!