微信小程序之canvas绘制海报

2019-09-05 21:44:38  阅读 107 次 评论 0 条
上一张效果图:

TIM图片20190904153410.png


第一步:配置合法域名

微信小程序绑定的微信号登录微信公众号,配置好uploadFile 域名,否则会报错说配置的uploadFile域名列表里没有你的域名.


第二步:把网络图片下载到微信本地

图片要提前下载完之后再绘图,不然图片显示不出来,用到的方法是:

wx.downloadFile({
      url: imgUrl, 
      success:res=>{
        if (res.statusCode === 200) {
          this.data.tempSrc= res.tempFilePath; //下载成功返回结果
        }
      }
 })

第三步:绘制

//开始用canvas绘制分享海报
  drawCanvas(fn) {
    const ctx = wx.createCanvasContext('myCanvas'); //创建画布
    wx.createSelectorQuery().select('#canvas-container').boundingClientRect(rect=>{
   
      let width = rect.width;
      let height = rect.height;
      let right = rect.right;
      let left = 20;

      ctx.setFillStyle('#fff');               // 填充画布为白色
      ctx.fillRect(0, 0, rect.width, height); // 画一个矩形 两个坐标
      let posterHeight = 400;                 // 海报高度 总共600
      let avatarWidth = width/7;              // 头像宽度
      let avatarHeight = avatarWidth;         // 头像宽度

      //海报
      if (this.data.posterSrc) {
        ctx.drawImage(this.data.posterSrc, 0, 0, width, posterHeight);
      }

      //姓名
      if (this.data.Name) {
        var name = this.data.Name
        let maxNameWidth = width - avatarHeight
        let maxNameLength = parseInt(maxNameWidth / 16) - 1
        if (maxNameLength < name.length) {
          name = name.substring(0, maxNameLength) + '...'
        }
        ctx.font = 'normal bold 16px sans-serif';
        ctx.setFontSize(16);
        ctx.setFillStyle('#000');
        ctx.setTextAlign('left');
        ctx.fillText(name, left + avatarWidth + 15, posterHeight + 38, width - avatarHeight - 40 - 15);
      }

      // 头像
      if (this.data.avatarSrc) {
        ctx.drawImage(this.data.avatarSrc, left, posterHeight + 20, avatarHeight, avatarHeight)
        ctx.setFontSize(10);
        ctx.setFillStyle('#000');
      }

      //职位
      if (this.data.Position) {
        var position = this.data.Position
        let maxPositionWidth = width - 70
        let maxPositionLength = parseInt(maxPositionWidth / 12) - 2
        if (maxPositionLength < position.length) {
          position = position.substring(0, maxPositionLength) + '...'
        }
        ctx.setFontSize(12);
        ctx.setFillStyle('#676b6d');
        ctx.setTextAlign('left');
        ctx.fillText(position, left + avatarWidth + 15, posterHeight + 58);
      }

      //电话
      if (this.data.Mobile) {
        ctx.drawImage(this.data.phoneIconSrc, left, posterHeight + avatarHeight + 60, 15, 15);
        ctx.setFontSize(12);
        ctx.setFillStyle('#666');
        ctx.setTextAlign('left');
        ctx.fillText(this.data.Mobile, left + 20, posterHeight + avatarHeight + 73);
      }

      // 公司名称
      if (this.data.Company) {
        var companyName = this.data.Company
        let maxCompanyNameWidth = width - 120
        // console.log(maxCompanyNameWidth)
        let maxCompanyNameLength = parseInt(maxCompanyNameWidth / 12) - 1
        // console.log(maxCompanyNameLength)
        const CONTENT_ROW_LENGTH = maxCompanyNameLength * 2; // 正文 单行显示字符长度
        ctx.drawImage(this.data.addressIconSrc, left, posterHeight + avatarHeight + 85, 15, 15);
        let [contentLeng, contentArray, contentRows] = this.textByteLength(this.data.Company, CONTENT_ROW_LENGTH);
        ctx.setFontSize(12);
        ctx.setFillStyle('#666');
        ctx.setTextAlign('left');
        let contentHh = 22 * 1;
        for (let m = 0; m < contentArray.length; m++) {
          ctx.fillText(contentArray[m], left + 20, posterHeight + avatarHeight + 100 + contentHh * m);
        }
      }

      //  绘制二维码
      if (this.data.qrSrc) {
        ctx.drawImage(this.data.qrSrc, width - 90, posterHeight + avatarHeight + 30, 70, 70)
        ctx.setFontSize(8);
        ctx.setFillStyle('#6f7475');
        ctx.fillText("扫一扫或长按识别", width - 88, posterHeight + avatarHeight + 115);
      }
      ctx.draw();
      wx.hideLoading();
      fn && fn()
    }).exec()
  },
  /**
   * 多行文字处理,每行显示数量
   * @param text 为传入的文本
   * @param num  为单行显示的字节长度
   */
  textByteLength(text, num) {
    let strLength = 0; // text byte length
    let rows = 1;
    let str = 0;
    let arr = [];
    for (let j = 0; j < text.length; j++) {
      if (text.charCodeAt(j) > 255) {
        strLength += 2;
        if (strLength > rows * num) {
          strLength++;
          arr.push(text.slice(str, j));
          str = j;
          rows++;
        }
      } else {
        strLength++;
        if (strLength > rows * num) {
          arr.push(text.slice(str, j));
          str = j;
          rows++;
        }
      }
    }
    arr.push(text.slice(str, text.length));
    // console.log([strLength, arr, rows])
    return [strLength, arr, rows] //  [处理文字的总字节长度,每行显示内容的数组,行数]
  }
附上此效果图的全部代码:

wxml

ps:本人用的是colorUI,当然样式不重要,都是可以自己写出来的

<view class='poste_box' id='canvas-container'>
  <canvas canvas-id="myCanvas">
  </canvas>
</view>
<view class="btn-container">
  <button class="cu-btn block bg-mauve lg shadow-blur" catchtap="drawLasted">最新海报</button>
  <button class="cu-btn block bg-orange lg shadow-blur" catchtap="drawRandom">随机海报</button>
  <button class="cu-btn block bg-blue lg shadow-blur" catchtap="drawDefault">自定义海报</button>
  <button class="cu-btn block bg-green lg shadow-blur" catchtap="saveShareImg" >保存海报</button>
</view>

wxss

page {
  display: flex;
  flex-direction: column;
}
.btn-container{
  padding: 20rpx 30rpx 40rpx 30rpx;
  display: flex;
  justify-content: space-between;
}
.btn-container button{
  width: 160rpx;
  height: 60rpx!important;
  line-height: 60rpx;
  font-size: 24rpx!important;
  padding: 0!important;
  text-align: center;
  border-radius: 32rpx;
}
canvas{
  width:calc(100% - 30rpx);
  height:1200rpx;
  margin: 20rpx auto;
}

js

const app = getApp()
Page({
  data:{
    avatar: '',                     //用户头像
    qrCode: "",                     //需要https图片路径
    Name: '',                       //姓名
    Position: "",                   //职位
    Mobile: "",                     //手机
    Company: "",                    //公司
    posterSrc: '',                  //海报图片
    avatarSrc: '',                  //用户头像
    phoneIconSrc: '',               //电话图标
    addressIconSrc: '',             //地址图标
    qrSrc: '',                      //二维码
    token: "",                      //七牛云token
    flag: true,                     //相机授权标识 默认授权 true
  },
  onLoad(options){
    this.data.card_id = options.card_id
    this.donwloadImg('https://pimg.llwangpu.com//1566886354972-8293', "phoneIconSrc")
    this.donwloadImg('https://pimg.llwangpu.com//1566886425694-8615', "addressIconSrc")
    app.apis.getQiniuToken().then(data => {this.data.token = data})
    this.init()
  },
  init(){
    wx.showLoading({ title: '生成中...', mask: true })
    //初始化海报
    this.initPosterInfo(1,()=>{
      //初始化名片信息
      this.initCardInfo(() => {
        //初始化二维码信息
        this.initQrInfo(() => {
          wx.hideLoading()
          //开始绘制海报
          this.drawCanvas()
        })
      })
    })
  },
  //初始化名片信息
  initCardInfo(fn){
    app.apis.getCardDetail(this.data.card_id).then(data => {
      this.donwloadImg(decodeURI(data.card_info.avatar), "avatarSrc",()=>{
        this.setData({
          avatar: data.card_info.avatar,
          Name: data.card_info.name,
          Position: data.card_info.position,
          Mobile: data.card_info.mobile_phone_number,
          Company: data.card_info.company_name
        })
        fn && fn()
      })
    })
  },
  //初始化二维码
  initQrInfo(fn){
    app.apis.getQr(1, this.data.card_id).then(res => {
      this.setData({ qrCode: res.url})
      this.donwloadImg(res.url, "qrSrc",()=>{
        fn && fn()
      })
    })
  },
  //初始化海报
  initPosterInfo(type,fn){
    app.apis.getPoster(type).then(res=>{
      this.donwloadImg(res.image_url, "posterSrc",()=>{
        fn && fn()
      })
    })
  },
  //初始化图片为本地图片
  donwloadImg(imgUrl,resultSrc,callback) {
    wx.downloadFile({
      url: imgUrl, 
      success:res=>{
        if (res.statusCode === 200) {
          this.data[resultSrc] = res.tempFilePath; //下载成功返回结果
          callback && callback()
        }
      }
    })
  },
  //开始用canvas绘制分享海报
  drawCanvas(fn) {
    const ctx = wx.createCanvasContext('myCanvas'); //创建画布
    wx.createSelectorQuery().select('#canvas-container').boundingClientRect(rect=>{
   
      let width = rect.width;
      let height = rect.height;
      let right = rect.right;
      let left = 20;

      ctx.setFillStyle('#fff');               // 填充画布为白色
      ctx.fillRect(0, 0, rect.width, height); // 画一个矩形 两个坐标
      let posterHeight = 400;                 // 海报高度 总共600
      let avatarWidth = width/7;              // 头像宽度
      let avatarHeight = avatarWidth;         // 头像宽度

      //海报
      if (this.data.posterSrc) {
        ctx.drawImage(this.data.posterSrc, 0, 0, width, posterHeight);
      }

      //姓名
      if (this.data.Name) {
        var name = this.data.Name
        let maxNameWidth = width - avatarHeight
        let maxNameLength = parseInt(maxNameWidth / 16) - 1
        if (maxNameLength < name.length) {
          name = name.substring(0, maxNameLength) + '...'
        }
        ctx.font = 'normal bold 16px sans-serif';
        ctx.setFontSize(16);
        ctx.setFillStyle('#000');
        ctx.setTextAlign('left');
        ctx.fillText(name, left + avatarWidth + 15, posterHeight + 38, width - avatarHeight - 40 - 15);
      }

      // 头像
      if (this.data.avatarSrc) {
        ctx.drawImage(this.data.avatarSrc, left, posterHeight + 20, avatarHeight, avatarHeight)
        ctx.setFontSize(10);
        ctx.setFillStyle('#000');
      }

      //职位
      if (this.data.Position) {
        var position = this.data.Position
        let maxPositionWidth = width - 70
        let maxPositionLength = parseInt(maxPositionWidth / 12) - 2
        if (maxPositionLength < position.length) {
          position = position.substring(0, maxPositionLength) + '...'
        }
        ctx.setFontSize(12);
        ctx.setFillStyle('#676b6d');
        ctx.setTextAlign('left');
        ctx.fillText(position, left + avatarWidth + 15, posterHeight + 58);
      }

      //电话
      if (this.data.Mobile) {
        ctx.drawImage(this.data.phoneIconSrc, left, posterHeight + avatarHeight + 60, 15, 15);
        ctx.setFontSize(12);
        ctx.setFillStyle('#666');
        ctx.setTextAlign('left');
        ctx.fillText(this.data.Mobile, left + 20, posterHeight + avatarHeight + 73);
      }

      // 公司名称
      if (this.data.Company) {
        var companyName = this.data.Company
        let maxCompanyNameWidth = width - 120
        // console.log(maxCompanyNameWidth)
        let maxCompanyNameLength = parseInt(maxCompanyNameWidth / 12) - 1
        // console.log(maxCompanyNameLength)
        const CONTENT_ROW_LENGTH = maxCompanyNameLength * 2; // 正文 单行显示字符长度
        ctx.drawImage(this.data.addressIconSrc, left, posterHeight + avatarHeight + 85, 15, 15);
        let [contentLeng, contentArray, contentRows] = this.textByteLength(this.data.Company, CONTENT_ROW_LENGTH);
        ctx.setFontSize(12);
        ctx.setFillStyle('#666');
        ctx.setTextAlign('left');
        let contentHh = 22 * 1;
        for (let m = 0; m < contentArray.length; m++) {
          ctx.fillText(contentArray[m], left + 20, posterHeight + avatarHeight + 100 + contentHh * m);
        }
      }

      //  绘制二维码
      if (this.data.qrSrc) {
        ctx.drawImage(this.data.qrSrc, width - 90, posterHeight + avatarHeight + 30, 70, 70)
        ctx.setFontSize(8);
        ctx.setFillStyle('#6f7475');
        ctx.fillText("扫一扫或长按识别", width - 88, posterHeight + avatarHeight + 115);
      }
      ctx.draw();
      wx.hideLoading();
      fn && fn()
    }).exec()
  },
  /**
   * 多行文字处理,每行显示数量
   * @param text 为传入的文本
   * @param num  为单行显示的字节长度
   */
  textByteLength(text, num) {
    let strLength = 0; // text byte length
    let rows = 1;
    let str = 0;
    let arr = [];
    for (let j = 0; j < text.length; j++) {
      if (text.charCodeAt(j) > 255) {
        strLength += 2;
        if (strLength > rows * num) {
          strLength++;
          arr.push(text.slice(str, j));
          str = j;
          rows++;
        }
      } else {
        strLength++;
        if (strLength > rows * num) {
          arr.push(text.slice(str, j));
          str = j;
          rows++;
        }
      }
    }
    arr.push(text.slice(str, text.length));
    // console.log([strLength, arr, rows])
    return [strLength, arr, rows] //  [处理文字的总字节长度,每行显示内容的数组,行数]
  },
  //最新海报
  drawLasted(){
    wx.showLoading({ title: '生成中...', mask: true })
    app.apis.getPoster(1).then(res => {
      this.donwloadImg(decodeURI(res.image_url), "posterSrc",()=>{
        this.drawCanvas(()=>{
          wx.hideLoading()
        })
      })
    })
  },
  //随机海报
  drawRandom(){
    wx.showLoading({ title: '生成中...', mask: true })
    app.apis.getPoster(2).then(res => {
      this.donwloadImg(decodeURI(res.image_url), "posterSrc", ()=>{
        this.drawCanvas(()=>{
          wx.hideLoading()
        })
      })
    })
  },
  //自定义海报
  drawDefault(){
    app.tools.uploadImage(this.data.token).then(res => {
      this.donwloadImg(res.imageURL, "posterSrc",()=>{
        this.drawCanvas()
      })
    })
  },
  //保存海报
  saveShareImg(){
  
    if (this.data.flag){
      wx.showLoading({title: '正在保存',mask: true})
      wx.canvasToTempFilePath({
        canvasId: 'myCanvas',
        success: res => {
          wx.hideLoading();
          wx.saveImageToPhotosAlbum({
            filePath: res.tempFilePath,
            success:res => {
              wx.showModal({
                title:'提示',
                content: '图片已保存到相册,赶紧晒一下吧~',
                showCancel: false,
                confirmText: '好的',
                confirmColor: '#333',
                success: function (res) {
                  if (res.confirm) { }
                },
                fail: function (res) { }
              })
            },
            fail:res => {
              wx.showToast({
                title: '保存失败,再次点击授权',
                icon: 'none',
                duration: 2000
              })
              this.data.flag = false
            }
          })
        }
      });
    }else{
      wx.openSetting({
        success: settingdata => {
          // console.log(settingdata)
          if (settingdata.authSetting['scope.writePhotosAlbum']) {
            // console.log('获取权限成功,给出再次点击图片保存到相册的提示。')
            this.data.flag = true
          } else {
            // console.log('获取权限失败,给出不给权限就无法正常使用的提示')
            this.data.flag = false
          }
        }
      })
    }
  }
})
PS:

海报需要单个接口请求,头像、名字、职位、电话、公司需要一个接口初始化,二维码需要一个接口请求,由于个人项目实际情况,采用的是回调函数方式调用实现。
实现了最新海报、随机海报、自定义海报、保存海报(拒绝授权保存到相册的合理处理),app.tools.uploadImage 是封装的自定义上传海报,利用七牛云上传实现自定义上传

本文地址:https://www.yangguangdream.com/?id=2044
版权声明:本文为原创文章,版权归 编辑君 所有,欢迎分享本文,转载请保留出处!

发表评论


表情

还没有留言,还不快点抢沙发?