小程序使用canvas标签生成海报并保存图片

先说一下做功能前的感受,简直一脸懵逼,第一次用canvas,只知道是个画布,其余什么都不知道…琢磨了一天才画出来…
开始之前百度了很久,想看看别人怎么写的,但是目前网上基本上用的都是wx.createCanvasContext这个api,但是这个api已经停止维护了,要求使用canvas代替,
小程序使用canvas标签生成海报并保存图片_第1张图片

 https://developers.weixin.qq.com/miniprogram/dev/api/canvas/wx.createCanvasContext.html
上面是官方文档的出处,只好开始硬怼canvas文档,言归正传,下面是我的代码。
第一步是先创建canvas标签;
 


第二步是获取手机系统信息,拿到手机屏幕的宽高以及设备像素比

  getSystemInfo() {
    let that = this
    wx.getSystemInfo({
      success: function (res) {
        console.log(res);
        that.setData({
          windowW: res.windowWidth,
          windowH: res.windowHeight,
          pixelRatio: res.pixelRatio
        })
      },
    })
  },

第三步是拿到canvas对象实例返回用于在画布上的环境,这里拿canvas节点实例用到wx.createSelectorQuery()这个api,拿到画布后就可以开始插入图片,绘制矩形等操作,这里要注意小程序要先缩放画布,如果不缩放,会导致画布过大不适应手机屏幕,canvas的像素单位是px,所以不能自适应屏幕,需要做换算,我换算的原理是设计稿的宽度是750,所以我按照设计稿的宽度进行换算,后来听我师傅说用rem也可以,但是懒得换了,就没换了哈哈哈。
 

  createcanvas: function () {
    this.setData({
      isshow: true
    })
    let that = this,
      windowW = that.data.windowW,
      windowH = that.data.windowH;
    const query = wx.createSelectorQuery() //获取对象实例
    query.select('#Canvas') //选择获取canvas对象实例
      .fields({ //获取节点的相关信息。需要获取的字段在fields中指定。
        node: true,
        size: true
      })
      .exec((res) => {
        console.log(res);
        const canvas = res[0].node //拿到canvas对象
        that.setData({ //这里保存canvas对象是因为下面保存相片要用这个对象
          canvas
        })
        const ctx = canvas.getContext('2d') //返回用于在画布上绘图的环境
        const dpr = that.data.pixelRatio //拿到设备像素比
        canvas.width = res[0].width * dpr //缩放画布
        canvas.height = res[0].height * dpr
        ctx.scale(dpr, dpr)

        //1、绘制背景图(本地图片)
        const imgbg = canvas.createImage()
        imgbg.onload = (e) => {
          console.log(1);
          ctx.drawImage(imgbg, 0.133 * windowW, (windowW - 100) * 0.2, (windowW - 100), (windowW - 100) * 1.38) //50
          //绘制上半部内容背景区域
          that.roundRect(ctx, 0.16 * windowW, (windowW - 100) * 0.25, (windowW - 122), (windowW - 100) * 0.89, 8, '#fff') // 59
          //绘制下半部内容背景区域
          that.roundRect(ctx, 0.16 * windowW, (windowW - 100) * 1.19, (windowW - 122), (windowW - 100) * 0.33, 8, '#fff') //59

          //2、绘制商品图片及文字
          const imggood = canvas.createImage()
          imggood.onload = (e) => {
            console.log(2);
            ctx.drawImage(imggood, 0.205 * windowW, (windowW - 100) * 0.28, (windowW - 154), (windowW - 100) * 0.61)
            //设置文字
            let str = '50元购100元代金券,任何商品可使用。具体解释权归商场所有。'
            let chr = str.split(""); //这个方法是将一个字符串分割成字符串数组
            let temp = "";
            let row = [];
            ctx.font = "12px";
            ctx.fillStyle = "#313648";
            // console.log(ctx.measureText(str)); //查看文本长度
            //如果文字过长设置为两行
            for (let a = 0; a < chr.length; a++) {
              if (ctx.measureText(temp).width < (windowW - 184)) {
                temp += chr[a];
              } else {
                a--; //这里添加了a-- 是为了防止字符丢失,效果图中有对比
                row.push(temp);
                temp = "";
              }
            }
            row.push(temp);
            if (row.length > 2) {
              let rowCut = row.slice(0, 2);
              let rowPart = rowCut[1];
              let test = "";
              let empty = [];
              for (let a = 0; a < rowPart.length; a++) {
                if (ctx.measureText(test).width < (windowW - 184)) {
                  test += rowPart[a];
                } else {
                  break;
                }
              }
              empty.push(test);
              let group = empty[0] + "..." //这里只显示两行,超出的用...表示
              rowCut.splice(1, 1, group);
              row = rowCut;
            }
            for (let b = 0; b < row.length; b++) {
              // console.log(row[b]);
              ctx.fillText(row[b], 0.23 * windowW, (windowW - 100) * 1 + b * 20); //85
            }
            //3、绘制底部数据
            const imgava = canvas.createImage()
            imgava.onload = (e) => {
              console.log(3);
              //绘制头像
              ctx.drawImage(imgava, 0.24 * windowW, (windowW - 100) * 1.24, windowW * 0.06, windowW * 0.06) // 90
              //绘制微信名
              ctx.font = "12px";
              ctx.fillStyle = "#313648";
              ctx.fillText('梅梅', 0.333 * windowW, (windowW - 100) * 1.29); //125

            }
            imgava.src = '../../images/phone.png'
            //4、绘制抢购价图片
            const imgqgj = canvas.createImage()
            imgqgj.onload = (e) => {
              console.log(4);
              //绘制头像
              ctx.drawImage(imgqgj, 0.24 * windowW, (windowW - 100) * 1.35, 0.101 * windowW, 0.04 * windowW) //90
              //绘制价格
              ctx.font = "14px sans-serif";
              ctx.fillStyle = "#e62e0a";
              ctx.fillText('¥98.00', 0.368 * windowW, (windowW - 100) * 1.395); //138
              //绘制喜欢的人
              ctx.font = "12px sans-serif";
              ctx.fillStyle = "#e62e0a";
              ctx.fillText('11846人喜欢', 0.24 * windowW, (windowW - 100) * 1.47); //90
              //绘制小程序二维码图片
              const imgqrcode = canvas.createImage()
              imgqrcode.onload = (e) => {
                console.log(5);
                //绘制二维码
                ctx.drawImage(imgqrcode, 0.64 * windowW, (windowW - 100) * 1.225, 0.144 * windowW, 0.144 * windowW)
                //绘制长按购买
                that.roundRect(ctx, 0.624 * windowW, (windowW - 100) * 1.44, 0.182 * windowW, 0.043 * windowW, 16, '#d94413')
                //绘制文字
                ctx.font = "10px sans-serif";
                ctx.fillStyle = "#fff";
                ctx.fillText('长按立即购买', 0.64 * windowW, (windowW - 100) * 1.48);
              }
              imgqrcode.src = '../../images/2ava.jpg'
            }
            imgqgj.src = '../../images/qgj.png'
          }
          imggood.src = '../../images/good.png'
        }
        imgbg.src = '../../images/1bg.png'
      })
  },


  /**
   * 绘制圆角矩形
   * @param {Object} ctx - canvas组件的绘图上下文
   * @param {Number} x - 矩形的x坐标
   * @param {Number} y - 矩形的y坐标
   * @param {Number} w - 矩形的宽度
   * @param {Number} h - 矩形的高度
   * @param {Number} r - 矩形的圆角半径
   * @param {String} [c = 'transparent'] - 矩形的填充色
   */
  roundRect(ctx, x, y, w, h, r, c = '#fff') {
    if (w < 2 * r) {
      r = w / 2;
    }
    if (h < 2 * r) {
      r = h / 2;
    }

    ctx.beginPath();
    ctx.fillStyle = c;

    ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
    ctx.moveTo(x + r, y);
    ctx.lineTo(x + w - r, y);
    ctx.lineTo(x + w, y + r);

    ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
    ctx.lineTo(x + w, y + h - r);
    ctx.lineTo(x + w - r, y + h);

    ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
    ctx.lineTo(x + r, y + h);
    ctx.lineTo(x, y + h - r);

    ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
    ctx.lineTo(x, y + r);
    ctx.lineTo(x + r, y);

    ctx.fill();
    ctx.closePath();
  },

代码很长,上面的两处代码我是百度两位博主粘贴得来的,比如文字过长分为两段显示的代码、绘制圆角矩形的代码,但是真不记得是哪位博主了…当时并没有做记录,所以如果你看到了,请谅解…
另外我这里的图片都是线下的图片,不是线上请求的,因为这个时候后端接口还没写好,后期线上图片有了再更新

第四步是保存图片到手机
这里主要用到三个api,第一个是wx.canvasToTempFilePath(把当前画布指定区域的内容导出生成指定大小的图片),第二个wx.getSetting(这里是查看是否有相册权限),第三个是wx.saveImageToPhotosAlbum(保存到相册),这里canvas生成图片对于2d画布使用的是canvas属性,而不是canvasId属性,要求传一个对象 这里就是我们在获取canvas实例的时候保存的canvas,下面是代码。

  //点击保存海报
  savepic() {
    let that = this
    wx.showLoading({
      title: '正在保存',
      mask: true
    })
    console.log(that.data.windowW - 0.133 * that.data.windowW, that.data.windowY - (that.data.windowW - 100) * 0.2);
    wx.canvasToTempFilePath({
      canvas: that.data.canvas,
      x: 0.133 * that.data.windowW,
      y: (that.data.windowW - 100) * 0.2,
      width: that.data.windowW - 100,
      height: (that.data.windowW - 100) * 1.38,
      success: function (res) {
        console.log(res);
        wx.hideLoading()
        let tempFilePath = res.tempFilePath
        that.checkWritePhotosAlbum(tempFilePath)
      },
      fail: function (err) {
        console.log(err)
        wx.showToast({
          title: '保存失败!请重新保存',
          icon: 'none'
        })
        wx.hideLoading()
      }
    })

  },
  //查看是否有相册权限
  checkWritePhotosAlbum: function (filePath) {
    let that = this
    wx.getSetting({
      success(res) {
        if (!res.authSetting['scope.writePhotosAlbum']) {
          wx.authorize({
            scope: 'scope.writePhotosAlbum',
            success() {
              that.saveToPhoto(filePath)
            },
            fail() {
              wx.openSetting({
                success(res) {
                  if (res.authSetting['scope.writePhotosAlbum']) {
                    that.saveToPhoto(filePath)
                  }
                },
                fail(res) {
                  wx.showToast({
                    title: '您没有授权,无法保存到相册!',
                    icon: 'none'
                  })
                }
              })
            }
          })
        } else {
          that.saveToPhoto(filePath)
        }
      }
    })
  },

  //保存到相册
  saveToPhoto: function (filePath) {
    let that = this;
    wx.saveImageToPhotosAlbum({
      filePath: filePath,
      success(res) {
        wx.showModal({
          content: '图片已保存到相册,前往相册即可查看!',
          showCancel: false,
          confirmText: '好的',
          confirmColor: '#333',
          success: function (res) {
            console.log(res)
            that.close()
          },
          fail: function (res) {
            console.log(res)
          }
        })
      },
      fail: function (res) {
        wx.showToast({
          title: res.errMsg,
          icon: 'none',
          duration: 2000
        })
      }
    })
  },


 

你可能感兴趣的:(微信小程序,微信小程序,前端)