微信小程序canvas绘制海报并保存本地相册

  • 作者: 凯哥Java
  • 经验分享
  • 时间:2019-08-02 17:16
  • 1186人已阅读
简介 在做微信小程序电商项目中,想要分享一款商品,使用最多并且最简便的方法就是使用小程序自带的分享api进行分享,但是分享出去的页面比较难看;    另一种方法就是自己使用小程序canvas绘制分享的海报,这个海报可以保存在相册里,而且可以按照自己的需求效果进行页面绘制。        

在做微信小程序电商项目中,想要分享一款商品,使用最多并且最简便的方法就是使用小程序自带的分享api进行分享,但是分享出去的页面比较难看;


        另一种方法就是自己使用小程序canvas绘制分享的海报,这个海报可以保存在相册里,而且可以按照自己的需求效果进行页面绘制。

e58c7b0d90c0ecaaf2f83e97cf65b630.gif

                                                         




 前提准备:


1、一张海报背景图、商品图片、长按识别小程序码,这三个图片必须是线上图片,然后使用wx.downloadFile将线上地址转化成本地地址,不然会导致真机不显示的问题;


2、域名配置,图片所涉及的域名一定要在小程序后台进行配置,开发的过程中虽然可以配置不校验域名,但是真正上线测试的时候一定要记得配置,不然在测试的时候会发现总是需要你去打开调试模式,有点烦人;


代码:


<view class="btn" bindtap="clickMe">点我生成海报</view>

<view wx:if="{{ show }}" class="canvas-box">

  <canvas style="width: {{ windowW }}px; height: {{ windowH }}px;" canvas-id="firstCanvas"></canvas>

  <view bindtap="daochu" class="btnSave">点击保存</view>

</view>

.btn{

  text-align: center;

  margin-top: 300rpx;

}

.canvas-box{

  background: #999999;

  position: fixed;

  top: 0;

}

 

.btnSave{

  z-index: 11111;

  font-size: 26rpx;

  background: #564d60;

  color: #ffffff;

  width: 200rpx;

  height: 70rpx;

  line-height: 70rpx;

  border-radius: 10rpx;

  text-align: center;

  position: absolute;

  bottom: 5rpx;

  left: 50%;

  margin-left: -100rpx;

}

 js逻辑思路:


1、在data中默认show为false,目的是解决canvas-box遮挡 “点我生成海报” 按钮,因为 canvas-box 样式设置了position: fixed;top:0;


2、当页面加载的时候获取设备的屏幕宽高,目的是为了让海报背景能始终居中于设备中心,后面绘制画布的时候会看到元素的位置几乎都用设备宽高进行了计算;同时,将线上图片url转换为本地地址,我的图片都是网上找的,如果真正项目要用的话,使替换成后台返回的图片路径即可;


3、点击 "点我生成海报" 按钮,调用 clickMe 方法,开始绘制海报;


4、海报绘制函数中调用了 newLine(text, context) ,目的值设置多文字折行功能;


5、点击 "点击保存" 按钮,触发daochu( )方法,使用wx.canvasToTempFilePath将画布生成指定大小的图片;


6、接5,生成成功后调用eventSave( )进行图片本地保存,使用的api是wx.saveImageToPhotosAlbum;


温馨提示:


js代码有点多,如果懒得看的童鞋们,不妨直接将代码下载下来在本地进行测试:


https://github.com/chenlun1000/wxCanvas.git


Page({

 

  /**

   * 页面的初始数据

   */

  data: {

    show: false

  },

 

  /**

   * 生命周期函数--监听页面加载

   */

  onLoad: function (options) {

    var that = this

 

    // 获取设备宽高,以备海报全屏显示

    wx.getSystemInfo({

      success: function (res) {

        that.setData({

          windowW: res.windowWidth,

          windowH: res.windowHeight

        })

      },

    })

 

    // 海报背景图线上地址

    var url = 'https://shop-1256250812.cos.ap-beijing.myqcloud.com/%E7%99%BD%E8%89%B2%E8%83%8C%E6%99%AF%E5%9B%BE.png'

    // 商品图片(哪吒头像)线上地址

    var urll = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1564567267739&di=a1ef7520ed6357e86f0433961f34886a&imgtype=0&src=http%3A%2F%2Ftvax1.sinaimg.cn%2Fcrop.0.0.1011.1011.1024%2F006MNQ2Qly8g531oqhnlyj30tf0sl7wh.jpg%3FExpires%3D1564330529%26ssig%3DkP54dSRQbD%26KID%3Dimgbed%2Ctva'

    // 小程序二维码

    var urlqCord = 'http://pic.qqtn.com/up/2019-7/2019073010080912409.jpg'

 

    that.getBG(url).then(function (locationData) {

      that.setData({

        bgpic: locationData

      })

    })

    that.getBG(urll).then(function (locationData) {

      that.setData({

        propic: locationData

      })

    })

    that.getBG(urlqCord).then(function (locationData) {

      that.setData({

        qCord: locationData

      })

    })

  },

 

  // 点击生成并保持海报到手机相册

  clickMe() {

    var that = this

    that.setData({

      show:true

    })

    that.drawCanvas()

    

  },

 

  // 绘制canvas

  drawCanvas() {

    var that = this

    var windowW = that.data.windowW

    var windowH = that.data.windowH

    var text = '从不拘泥任何世俗凡人的目光,我要奔向前方那光芒,生而为魔,那又如何'

    // 使用 wx.createContext 获取绘图上下文 context

    var context = wx.createCanvasContext('firstCanvas')

 

    // 海报背景图

    context.drawImage(that.data.bgpic, (windowW - 280) / 2, (windowH -450) / 2, 280, 450)

    // 商品图片

    context.drawImage(that.data.propic, (windowW -170) / 2, (windowH - 390) / 2, 170,170)

    // 商品文字描述

    context.setFontSize(30)

    context.setFillStyle("red")

    context.fillText('¥99.99', (windowW - 200) / 2, (windowH +55) / 2)

    context.setFontSize(18)

    context.setFillStyle("#999999")

    context.fillText('¥99.99', (windowW + 50) / 2, (windowH +55) / 2)

    context.moveTo((windowW + 45) / 2, (windowH + 44) / 2); //设置线条的起始路径坐标

    context.lineTo((windowW + 200) / 2, (windowH +44) / 2); //设置线条的终点路径坐标

    context.stroke(); //对当前路径进行描边

 

    // 商品名字,名字很长调用方法将文字折行,传参 文字内容text,画布context

    var row = that.newLine(text, context)

    var a = 24//定义行高25

    for (var i = 0; i < row.length; i++) {

      context.setFontSize(16)

      context.setFillStyle("#000000")

      context.fillText(row[i], (windowW - 195) / 2, (windowH +130)/2 + a * i, 320)

    }

 

    // 识别小程序二维码

    context.drawImage(that.data.qCord, (windowW - 180) / 2, (windowH + 289) / 2, 75, 75)

    context.setFillStyle("#000000")

    context.setFontSize(12)

    context.fillText('长按识别小程序', (windowW - 0) / 2, (windowH + 350) / 2 )

    context.setFillStyle("#000000")

    context.setFontSize(18)

    context.fillText('享更多好货', (windowW - 0) / 2, (windowH + 390) / 2 )

    context.draw()

  },

 

  // 点击保存按钮,同时将画布转化为图片

  daochu: function () {

    var that = this;

    wx.canvasToTempFilePath({

      x: 0,

      y: 0,

      canvasId: 'firstCanvas',

      fileType: 'jpg',

      quality: 1,

      success: function (res) {

        that.setData({

          shareImage: res.tempFilePath

        })

        setTimeout(function(){

          wx.showModal({

            title: '提示',

            content: '将生成的海报保存到手机相册,可以发送给微信好友或分享到朋友圈',

            success(res) {

              if (res.confirm) {

                that.eventSave()

              } else if (res.cancel) {

                console.log('用户点击取消')

              }

            }

          })

        },1000)

      }

    })

  },

 

  // 将商品分享图片保存到本地

  eventSave() {

    wx.saveImageToPhotosAlbum({

      filePath: this.data.shareImage,

      success(res) {

        wx.showToast({

          title: '保存图片成功',

          icon: 'success',

          duration: 2000

        })

      }

    })

  },

 

  //将线上图片地址下载到本地,此函数进行了封装,只有在需要转换url的时候调用即可

  getBG(url) {

    // Promise函数给我很大的帮助,让我能return出回调函数中的值

    return new Promise(function (resolve) {

      wx.downloadFile({

        url: url, 

        success: function (res) {

          url = res.tempFilePath

          resolve(url);

        }

      })

    })

  },

 

  // canvas多文字换行

  newLine(txt, context) {

    var txtArr = txt.split('')

    var temp = ''

    var row = []

    for (var i = 0; i < txtArr.length; i++) {

      if (context.measureText(temp).width < 210) {

        temp += txtArr[i]

      } else {

        i--

        row.push(temp)

        temp = ''

      }

    }

    row.push(temp)

 

    //如果数组长度大于3 则截取前三个

    if (row.length > 3) {

      var rowCut = row.slice(0, 3);

      var rowPart = rowCut[2];

      var test = "";

      var empty = [];

      for (var a = 0; a < rowPart.length; a++) {

        if (context.measureText(test).width < 180) {

          test += rowPart[a];

        } else {

          break;

        }

      }

      empty.push(test);

      var group = empty[0] + "..." //这里只显示三行,超出的用...表示

      rowCut.splice(2, 1, group);

      row = rowCut;

    }

 

    return row

  },

 

  /**

   * 生命周期函数--监听页面初次渲染完成

   */

  onReady: function () {

    var that = this

    

  },

 

  /**

   * 生命周期函数--监听页面显示

   */

  onShow: function () {

 

  },

 

  /**

   * 生命周期函数--监听页面隐藏

   */

  onHide: function () {

 

  },

 

  /**

   * 生命周期函数--监听页面卸载

   */

  onUnload: function () {

 

  }

})

总结:


1、getBG(url)方法:项目中多次进行wx.downloadFile线上图片路径转换成本地路径的方法,为了方便,我将此方法进行封装;


2、使用 Promise函数: 将回调函数中的值进行了return处理,能让外部接收到回调函数的值;


3、解决了canvas绘制的海报在保存到相册的时候出现图片模糊不清,字体有明显锯齿状的情况;


4、newLine(txt, context)方法:解决了画布上多行文字换行功能,并且可以按需设置超过几行后显示省略号;

--------------------- 

作者:闹闹的闹钟 

原文:https://blog.csdn.net/weixin_42157001/article/details/98068858 



Top Top