zoukankan      html  css  js  c++  java
  • 小程序canvas生成二维码图片踩的坑

    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="{{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, {
                 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,
                             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                          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="{{SIZE}}px;height:{{SIZE}}px;position: fixed;left: -500px; bottom: -500px;"></canvas>
     <canvas canvas-id="QRCode-canvas-1" style="{{SIZE}}px;height:{{SIZE}}px;position: fixed;left: -50px; bottom: -1000px;"></canvas>
     <canvas canvas-id="QRCode-canvas-2" style="{{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 的坑,你看到这里,这些坑你也在踩,刚好我踩过了,给你们参考,希望对你们有帮助!

  • 相关阅读:
    ZJU 1610
    zju1484
    字符串赋值与初始化
    内核线程、内核级线程(轻量级进程)和用户级线程
    Mysql基础
    结构体的sizeof
    对象属性值读取问题
    返回引用类型
    操作符重载为成员函数、非成员函数与友元函数的区别
    运算符优先级
  • 原文地址:https://www.cnblogs.com/l-yabiao/p/9125294.html
Copyright © 2011-2022 走看看