小顺序怎样天生海报分享朋友圈

项目需求写完有一段时间了,然则照样想回过来总结一下,一是对项目标回忆优化等,二是对坑的处所做个纪录,防止今后碰到相似的题目。

需求

运用微信壮大的社交能力通太小顺序到达裂变的目标,拉取新用户。
天生的海报以下

《小顺序怎样天生海报分享朋友圈》

需求剖析

1、运用小顺序官方供应的api能够直接分享转发到微信群翻开小顺序
2、运用小顺序天生海报保留图片到相册分享到朋友圈,用户长按辨认二维码关注民众号或许翻开小顺序来到达裂变的目标

完成计划

一、剖析怎样完成

置信人人应当都邑有相似的疑惑,就是怎样根据产品设计的那样绘制成海报,实在当时我也是不知道怎样动手,仔细想了下得经由历程canvas绘制成图片,如许用户保留这个图片到相册,就能够分享到朋友圈了。然则要绘制的图片上面不仅有笔墨另有数字、图片、二维码等且都是活的,这个要怎样动态天生呢。仔细想了下,须要一点一点的将笔墨和数字,背景图绘制到画布上去,如许经由历程api终究合成一个图片导出到手机相册中。

二、须要处置惩罚的题目

1、二维码的动态猎取和绘制(包含怎样天生小顺序二维码、民众号二维码、翻开网页二维码)
2、背景图怎样绘制,猎取图片信息
3、将绘制完成的图片保留到当地相册
4、处置惩罚用户是不是作废受权保留到相册

三、完成步骤

这里我详细写下缭绕上面所提出的题目,形貌也许完成的历程

①起首建立canvas画布,我把画布定位设成负的,是为了不让它显现在页面上,是由于我尝试把canvas经由历程推断前提动态的显现和隐蔽,在绘制的时刻会涌现题目,所以采用了这类要领,这里另有肯定要设置画布的大小。

<canvas canvas-id="myCanvas" style="width: 690px;height:1085px;position: fixed;top: -10000px;"></canvas>

②建立好画布以后,先绘制背景图,由于背景图我是放在当地,所以猎取 <canvas> 组件 canvas-id 属性,经由历程createCanvasContext建立canvas的画图上下文 CanvasContext 对象。运用drawImage绘制图象到画布,第一个参数是图片的当地地点,背面两个参数是图象相对画布左上角位置的x轴和y轴,末了两个参数是设置图象的宽高。

const ctx = wx.createCanvasContext('myCanvas')

ctx.drawImage('/img/study/shareimg.png', 0, 0, 690, 1085)

③建立好背景图后,在背景图上绘制头像,笔墨和数字。经由历程getImageInfo猎取头像的信息,这里须要注重下在猎取的收集图片要先设置download域名才见效,详细在小顺序背景设置里设置。

猎取头像地点,起首量取头像在画布中的大小,和x轴Y轴的坐标,这里的result[0]是我用promise封装返回的一个图片地点

let headImg = new Promise(function (resolve) {
        wx.getImageInfo({
          src: `${app.globalData.baseUrl2}${that.data.currentChildren.headImg}`,
          success: function (res) {
            resolve(res.path)
          },
          fail: function (err) {
            console.log(err)
            wx.showToast({
              title: '收集毛病请重试',
              icon: 'loading'
            })
          }
        })
      })
      
let avatarurl_width = 60, //绘制的头像宽度
    avatarurl_heigth = 60, //绘制的头像高度
    avatarurl_x = 28, //绘制的头像在画布上的位置
    avatarurl_y = 36; //绘制的头像在画布上的位置
    
    ctx.save(); // 先保留状况 已便于画完圆再用
    ctx.beginPath(); //最先绘制
    //先画个圆   前两个参数肯定了圆心 (x,y) 坐标  第三个参数是圆的半径  四参数是画图方向  默许是false,即顺时针
    ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math.PI * 2, false);
    ctx.clip(); //画了圆 再剪切  原始画布中剪切恣意外形和尺寸。一旦剪切了某个地区,则一切以后的画图都邑被限定在被剪切的地区内
    ctx.drawImage(result[0], avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth); // 推进去图片
    

这里举个例子说下怎样绘制笔墨,比方我要绘制以下这个“字”,须要动态猎取前面字数的总宽度,如许才设置“字”的x轴坐标,这里我本来是想经由历程
measureText来丈量字体的宽度,然则在iOS端第一次猎取的宽度值不对,关于这个题目,我还在微信开发者社区提了
bug,所以我想用另一个要领来完成,就是先猎取一般情况下一个字的宽度值,然后乘以总字数就获得了总宽度,亲试是能够的。

《小顺序怎样天生海报分享朋友圈》

let allReading = 97 / 6 / app.globalData.ratio * wordNumber.toString().length + 325;
ctx.font = 'normal normal 30px sans-serif';
ctx.setFillStyle('#ffffff')
ctx.fillText('字', allReading, 150);

④绘制民众号二维码,和猎取头像是一样的,也是先经由历程接口返回图片收集地点,然后再经由历程getImageInfo猎取民众号二维码图片信息

⑤怎样绘制小顺序码,详细官网文档也给出天生无穷小顺序码接口,经由历程天生的小顺序能够翻开恣意一个小顺序页面,而且二维码永远有效,详细挪用哪一个小顺序二维码接口有差别的运用场景,详细能够看下官方文档怎样说的,也就是说前端经由历程通报参数调取后端接口返回的小顺序码,然后绘制在画布上(和上面写的绘制头像和民众号二维码一样的)

ctx.drawImage('小顺序码的当地地点', x轴, Y轴, 宽, 高)

⑥终究绘制完把canvas画布转成图片并返回图片地点

        wx.canvasToTempFilePath({
            canvasId: 'myCanvas',
            success: function (res) {
              canvasToTempFilePath = res.tempFilePath // 返回的图片地点保留到一个全局变量里
              that.setData({
                showShareImg: true
              })
              wx.showToast({
                title: '绘制胜利',
              })
            },
            fail: function () {
              wx.showToast({
                title: '绘制失利',
              })
            },
            complete: function () {
              wx.hideLoading()
              wx.hideToast()
            }
          })

⑦保留到体系相册;先推断用户是不是开启用户受权相册,处置惩罚差别情况下的效果。比方用户假如根据一般逻辑受权是没题目标,然则有的用户假如点击了作废受权该怎样处置惩罚,假如不处置惩罚会涌现肯定的题目。所以当用户点击作废受权以后,来个弹框提醒,当它再次点击的时刻,主动跳到设置指导用户去开启受权,从而到达保留到相册分享朋友圈的目标。

// 猎取用户是不是开启用户受权相册
    if (!openStatus) {
      wx.openSetting({
        success: (result) => {
          if (result) {
            if (result.authSetting["scope.writePhotosAlbum"] === true) {
              openStatus = true;
              wx.saveImageToPhotosAlbum({
                filePath: canvasToTempFilePath,
                success() {
                  that.setData({
                    showShareImg: false
                  })
                  wx.showToast({
                    title: '图片保留胜利,快去分享到朋友圈吧~',
                    icon: 'none',
                    duration: 2000
                  })
                },
                fail() {
                  wx.showToast({
                    title: '保留失利',
                    icon: 'none'
                  })
                }
              })
            }
          }
        },
        fail: () => { },
        complete: () => { }
      });
    } else {
      wx.getSetting({
        success(res) {
          // 假如没有则猎取受权
          if (!res.authSetting['scope.writePhotosAlbum']) {
            wx.authorize({
              scope: 'scope.writePhotosAlbum',
              success() {
                openStatus = true
                wx.saveImageToPhotosAlbum({
                  filePath: canvasToTempFilePath,
                  success() {
                    that.setData({
                      showShareImg: false
                    })
                    wx.showToast({
                      title: '图片保留胜利,快去分享到朋友圈吧~',
                      icon: 'none',
                      duration: 2000
                    })
                  },
                  fail() {
                    wx.showToast({
                      title: '保留失利',
                      icon: 'none'
                    })
                  }
                })
              },
              fail() {
                // 假如用户谢绝过或没有受权,则再次翻开受权窗口
                openStatus = false
                console.log('请设置许可接见相册')
                wx.showToast({
                  title: '请设置许可接见相册',
                  icon: 'none'
                })
              }
            })
          } else {
            // 有则直接保留
            openStatus = true
            wx.saveImageToPhotosAlbum({
              filePath: canvasToTempFilePath,
              success() {
                that.setData({
                  showShareImg: false
                })
                wx.showToast({
                  title: '图片保留胜利,快去分享到朋友圈吧~',
                  icon: 'none',
                  duration: 2000
                })
              },
              fail() {
                wx.showToast({
                  title: '保留失利',
                  icon: 'none'
                })
              }
            })
          }
        },
        fail(err) {
          console.log(err)
        }
      })
    }

总结

至此一切的步骤都已完成,在绘制的时刻会碰到一些异步要求背景返回的数据,所以我用promise和async和await进行了封装,确保导出的图片信息是完全的。在绘制的历程确切碰到一些坑的处所。比方初最先导出的图片比例大小不对,另有效measureText丈量笔墨宽度不对,屡次绘制(能够受收集缘由)偶然导出的图片上的笔墨色彩会有误差等。假如你也碰到一些比较坑的处所能够一同讨论下做个纪录,下面附下完全的代码

import regeneratorRuntime from '../../utils/runtime.js' // 引入模块
const app = getApp(),
  api = require('../../service/http.js');
var ctx = null, // 建立canvas对象
    canvasToTempFilePath = null, // 保留终究天生的导出的图片地点
    openStatus = true; // 声明一个全局变量推断是不是受权保留到相册

// 猎取微信民众号二维码
  getCode: function () {
    return new Promise(function (resolve, reject) {
      api.fetch('/wechat/open/getQRCodeNormal', 'GET').then(res => {
        console.log(res, '猎取微信民众号二维码')
        if (res.code == 200) {
          console.log(res.content, 'codeUrl')
          resolve(res.content)
        }
      }).catch(err => {
        console.log(err)
      })
    })
  },

  // 天生海报
  async createCanvasImage() {
    let that = this;
    // 点击天生海报数据埋点
    that.setData({
      generateId: '点击天生海报'
    })
    if (!ctx) {
      let codeUrl = await that.getCode()
      wx.showLoading({
        title: '绘制中...'
      })
      let code = new Promise(function (resolve) {
        wx.getImageInfo({
          src: codeUrl,
          success: function (res) {
            resolve(res.path)
          },
          fail: function (err) {
            console.log(err)
            wx.showToast({
              title: '收集毛病请重试',
              icon: 'loading'
            })
          }
        })
      })
      let headImg = new Promise(function (resolve) {
        wx.getImageInfo({
          src: `${app.globalData.baseUrl2}${that.data.currentChildren.headImg}`,
          success: function (res) {
            resolve(res.path)
          },
          fail: function (err) {
            console.log(err)
            wx.showToast({
              title: '收集毛病请重试',
              icon: 'loading'
            })
          }
        })
      })

      Promise.all([headImg, code]).then(function (result) {
        const ctx = wx.createCanvasContext('myCanvas')
        console.log(ctx, app.globalData.ratio, 'ctx')
        let canvasWidthPx = 690 * app.globalData.ratio,
          canvasHeightPx = 1085 * app.globalData.ratio,
          avatarurl_width = 60, //绘制的头像宽度
          avatarurl_heigth = 60, //绘制的头像高度
          avatarurl_x = 28, //绘制的头像在画布上的位置
          avatarurl_y = 36, //绘制的头像在画布上的位置
          codeurl_width = 80, //绘制的二维码宽度
          codeurl_heigth = 80, //绘制的二维码高度
          codeurl_x = 588, //绘制的二维码在画布上的位置
          codeurl_y = 984, //绘制的二维码在画布上的位置
          wordNumber = that.data.wordNumber, // 猎取总浏览字数
          // nameWidth = ctx.measureText(that.data.wordNumber).width, // 猎取总浏览字数的宽度
          // allReading = ((nameWidth + 375) - 325) * 2 + 380;
          // allReading = nameWidth / app.globalData.ratio + 325;
          allReading = 97 / 6 / app.globalData.ratio * wordNumber.toString().length + 325;
        console.log(wordNumber, wordNumber.toString().length, allReading, '猎取总浏览字数的宽度')
        ctx.drawImage('/img/study/shareimg.png', 0, 0, 690, 1085)
        ctx.save(); // 先保留状况 已便于画完圆再用
        ctx.beginPath(); //最先绘制
        //先画个圆   前两个参数肯定了圆心 (x,y) 坐标  第三个参数是圆的半径  四参数是画图方向  默许是false,即顺时针
        ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math.PI * 2, false);
        ctx.clip(); //画了圆 再剪切  原始画布中剪切恣意外形和尺寸。一旦剪切了某个地区,则一切以后的画图都邑被限定在被剪切的地区内
        ctx.drawImage(result[0], avatarurl_x, avatarurl_y, avatarurl_width, avatarurl_heigth); // 推进去图片

        ctx.restore(); //恢复之前保留的画图上下文状况 能够继承绘制
        ctx.setFillStyle('#ffffff'); // 笔墨色彩
        ctx.setFontSize(28); // 笔墨字号
        ctx.fillText(that.data.currentChildren.name, 103, 78); // 绘制笔墨

        ctx.font = 'normal bold 44px sans-serif';
        ctx.setFillStyle('#ffffff'); // 笔墨色彩
        ctx.fillText(wordNumber, 325, 153); // 绘制笔墨

        ctx.font = 'normal normal 30px sans-serif';
        ctx.setFillStyle('#ffffff')
        ctx.fillText('字', allReading, 150);

        ctx.font = 'normal normal 24px sans-serif';
        ctx.setFillStyle('#ffffff'); // 笔墨色彩
        ctx.fillText('打败了全国', 26, 190); // 绘制笔墨

        ctx.font = 'normal normal 24px sans-serif';
        ctx.setFillStyle('#faed15'); // 笔墨色彩
        ctx.fillText(that.data.percent, 154, 190); // 绘制孩子百分比

        ctx.font = 'normal normal 24px sans-serif';
        ctx.setFillStyle('#ffffff'); // 笔墨色彩
        ctx.fillText('的小朋友', 205, 190); // 绘制孩子百分比

        ctx.font = 'normal bold 32px sans-serif';
        ctx.setFillStyle('#333333'); // 笔墨色彩
        ctx.fillText(that.data.singIn, 50, 290); // 签到天数

        ctx.fillText(that.data.reading, 280, 290); // 浏览时长
        ctx.fillText(that.data.reading, 508, 290); // 听书时长

        // 书本浏览构造
        ctx.font = 'normal normal 28px sans-serif';
        ctx.setFillStyle('#ffffff'); // 笔墨色彩
        ctx.fillText(that.data.bookInfo[0].count, 260, 510); 
        ctx.fillText(that.data.bookInfo[1].count, 420, 532); 
        ctx.fillText(that.data.bookInfo[2].count, 520, 594); 
        ctx.fillText(that.data.bookInfo[3].count, 515, 710); 
        ctx.fillText(that.data.bookInfo[4].count, 492, 828); 
        ctx.fillText(that.data.bookInfo[5].count, 348, 858); 
        ctx.fillText(that.data.bookInfo[6].count, 212, 828); 
        ctx.fillText(that.data.bookInfo[7].count, 148, 726); 
        ctx.fillText(that.data.bookInfo[8].count, 158, 600); 

        ctx.font = 'normal normal 18px sans-serif';
        ctx.setFillStyle('#ffffff'); // 笔墨色彩
        ctx.fillText(that.data.bookInfo[0].name, 232, 530); 
        ctx.fillText(that.data.bookInfo[1].name, 394, 552); 
        ctx.fillText(that.data.bookInfo[2].name, 496, 614); 
        ctx.fillText(that.data.bookInfo[3].name, 490, 730); 
        ctx.fillText(that.data.bookInfo[4].name, 466, 850); 
        ctx.fillText(that.data.bookInfo[5].name, 323, 878); 
        ctx.fillText(that.data.bookInfo[6].name, 184, 850); 
        ctx.fillText(that.data.bookInfo[7].name, 117, 746); 
        ctx.fillText(that.data.bookInfo[8].name, 130, 621); 

        ctx.drawImage(result[1], codeurl_x, codeurl_y, codeurl_width, codeurl_heigth); // 绘制头像
        ctx.draw(false, function () {
          // canvas画布转成图片并返回图片地点
          wx.canvasToTempFilePath({
            canvasId: 'myCanvas',
            success: function (res) {
              canvasToTempFilePath = res.tempFilePath
              that.setData({
                showShareImg: true
              })
              console.log(res.tempFilePath, 'canvasToTempFilePath')
              wx.showToast({
                title: '绘制胜利',
              })
            },
            fail: function () {
              wx.showToast({
                title: '绘制失利',
              })
            },
            complete: function () {
              wx.hideLoading()
              wx.hideToast()
            }
          })
        })
      })
    }
  },

  // 保留到体系相册
  saveShareImg: function () {
    let that = this;
    // 数据埋点点击保留学情海报
    that.setData({
      saveId: '保留学情海报'
    })
    // 猎取用户是不是开启用户受权相册
    if (!openStatus) {
      wx.openSetting({
        success: (result) => {
          if (result) {
            if (result.authSetting["scope.writePhotosAlbum"] === true) {
              openStatus = true;
              wx.saveImageToPhotosAlbum({
                filePath: canvasToTempFilePath,
                success() {
                  that.setData({
                    showShareImg: false
                  })
                  wx.showToast({
                    title: '图片保留胜利,快去分享到朋友圈吧~',
                    icon: 'none',
                    duration: 2000
                  })
                },
                fail() {
                  wx.showToast({
                    title: '保留失利',
                    icon: 'none'
                  })
                }
              })
            }
          }
        },
        fail: () => { },
        complete: () => { }
      });
    } else {
      wx.getSetting({
        success(res) {
          // 假如没有则猎取受权
          if (!res.authSetting['scope.writePhotosAlbum']) {
            wx.authorize({
              scope: 'scope.writePhotosAlbum',
              success() {
                openStatus = true
                wx.saveImageToPhotosAlbum({
                  filePath: canvasToTempFilePath,
                  success() {
                    that.setData({
                      showShareImg: false
                    })
                    wx.showToast({
                      title: '图片保留胜利,快去分享到朋友圈吧~',
                      icon: 'none',
                      duration: 2000
                    })
                  },
                  fail() {
                    wx.showToast({
                      title: '保留失利',
                      icon: 'none'
                    })
                  }
                })
              },
              fail() {
                // 假如用户谢绝过或没有受权,则再次翻开受权窗口
                openStatus = false
                console.log('请设置许可接见相册')
                wx.showToast({
                  title: '请设置许可接见相册',
                  icon: 'none'
                })
              }
            })
          } else {
            // 有则直接保留
            openStatus = true
            wx.saveImageToPhotosAlbum({
              filePath: canvasToTempFilePath,
              success() {
                that.setData({
                  showShareImg: false
                })
                wx.showToast({
                  title: '图片保留胜利,快去分享到朋友圈吧~',
                  icon: 'none',
                  duration: 2000
                })
              },
              fail() {
                wx.showToast({
                  title: '保留失利',
                  icon: 'none'
                })
              }
            })
          }
        },
        fail(err) {
          console.log(err)
        }
      })
    }
  },
    原文作者:小白
    原文地址: https://segmentfault.com/a/1190000019083548
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞