微信小程序开发微信小程序开发微信小程序开发者

小程序的canvas绘图的封装

2018-09-17  本文已影响24人  甚时跃马归来

背景:由于小程序没有直接分享到朋友圈的接口,但一些日常操作又需要用到分享到朋友圈等。于是,
只能采取“曲线救国”的路线,生成一张精美的卡片等,由用户分享朋友圈。

1.关于canvas的一些坑

2.canvas的封装

canvas绘制分享图片的操作可能在一个项目中要用上好几次,或者很多项目都需要用到。这时候我们就可以把它封装
起来,到用的时候直接调用。也省的每次都编写重复的代码,还可能出错。

2.1 制定目标

我们对canvas相关操作进行封装,其实是为了生成精美的分享图片(其中可能涉及到文字和图片的绘制)。
所以,目标我们有了。基于这个目标,我们大概需要如下属性。

2.2 敲定参数

因为要绘制图片或者文字。所以,除此之外,我们还需要,imgList和wordsList

imgList中,每个数据都应是1个需绘制图片的所有信息,所以它应该是json数据格式的,即{},wordsList同理
那么imgList就需要如下数据且都是必须传入的(如果传入图片的话)

同理,wordsList就需要如下数据,

由于wordsList中存在非必须传入的一些属性,当不传入时,那么就可能会出现一些问题。于是设定一些
可以复用的参数,接着前面的wordsList下来

至此,所需要的参数,基本敲定。但我们不可能在调用方法的时候传入十几个参数。所以,我们就把这些参数,
用{}包起来(也就是接收一个{}格式的参数),调用的时候把所有数据用{}包起来传进来即可。

2.3函数封装

新建js文件,开始封装函数

// data为{}格式数据,dosomething为回调函数
function gk_draw(data,dosomething){
    
}

新建验证函数,总共有3个。

// 验证offsetx,offsety,width,height,destWidth,destHeightd等必填参数
function checkCanvasToTempFileData(data){
  if ((!data.hasOwnProperty('offsetx')) || (typeof data.offsetx != 'number')){
    return false
  }
  if ((!data.hasOwnProperty('offsety')) || (typeof data.offsety != 'number')) {
    return false
  }
  if ((!data.hasOwnProperty('width')) || (typeof data.width != 'number')) {
    return false
  }
  if ((!data.hasOwnProperty('height')) || (typeof data.height != 'number'))  {
    return false
  }
  if ((!data.hasOwnProperty('destWidth')) || (typeof data.destWidth != 'number'))  {
    return false
  }
  if ((!data.hasOwnProperty('destHeight')) || (typeof data.destHeight != 'number'))  {
    return false
  }
  return true
}

// 验证imgList中的source,x,y,width,height等必填参数
function checkImgsInfo(info){
  if ((!info.hasOwnProperty('source')) || (typeof info.source != 'string')){
    return false
  }
  if ((!info.hasOwnProperty('x')) || (typeof info.x != 'number')) {
    return false
  }
  if ((!info.hasOwnProperty('y')) || (typeof info.y != 'number')) {
    return false
  }
  if ((!info.hasOwnProperty('width')) || (typeof info.width != 'number')) {
    return false
  }
  if ((!info.hasOwnProperty('height')) || (typeof info.height != 'number')) {
    return false
  }

  return true
}

// 验证word,x,y等必填参数
function checkWordsInfo(info){
  if ((!info.hasOwnProperty('x')) || (typeof info.x != 'number')) {
    return false
  }
  if ((!info.hasOwnProperty('y')) || (typeof info.y != 'number')) {
    return false
  }
  if ((!info.hasOwnProperty('word')) || (typeof info.word != 'string')) {
    return false
  }

  return true
}

1.在gk_draw中加入data的验证代码,如下

    // 先显示加载框
  wx.showLoading({
    title: '加载中...',
  })

    // 数据验证
  if (!checkCanvasToTempFileData(data)){
    //报错
    console.error('fail to draw , some params can not be empty')
    // 终止执行
    return 'fail'
  }
  
  // 获取必须参数
  let mycanvasid = data.canvasId
  let offsetx = data.offsetx
  let offsety = data.offsety
  let width = data.width
  let height = data.height
  let destWidth = data.destWidth
  let destHeight = data.destHeight

2.验证imgList和wordsList,因为canvas画图分享必然会绘制这两者之一。

  // 此处如果需要指定图片格式可自行加入相关代码
  // 及生成图片质量等

  let wordsList = []
  let imgList = []

  if (data.hasOwnProperty('wordsList') && (typeof data.wordsList == 'object')){// 如果存在文字数组
    
    wordsList = data.wordsList
  }

  if (data.hasOwnProperty('imgList') && typeof data.imgList == 'object') {// 如果存在图片数组
    
    imgList = data.imgList
  }

  let wlen = wordsList.length
  let ilen = imgList.length

  // 两者不能同时为空,事实上,绝大多数绘图都会同时绘制这两样元素
  if((wlen == 0) && (ilen == 0)){
    console.error('fail to draw , wordsList(array) and imgList(array) can not be empty both')
    return 'fail'
  }

3.绘制图片

  // 创建ctx
  let ctx = wx.createCanvasContext(mycanvasid, this)

  // 这里要先绘制图片,因为先绘制文字的话,图片会把文字覆盖掉
  if (ilen > 0){
    for (let i = 0; i < ilen; i++) {
      let theImgInfo = imgList[i]
      
      //验证数据
      if (!checkImgsInfo(theImgInfo)){
        console.error('fail to draw , some pramas of imgList can not be empty')
        return 'fail'
      }

      // 开始绘制图片
      ctx.drawImage(theImgInfo.source, theImgInfo.x, theImgInfo.y, theImgInfo.width, theImgInfo.height)

    }
  }

4.绘制文字

    if (wlen > 0){
        
        // 初始化一些数据
        let color = 'black'
        if (data.hasOwnProperty('textColor') && (typeof data.textColor == 'string')) {
          color = data.textColor
        }
    
        let font = 35
        if (data.hasOwnProperty('font') && (typeof data.font == 'number')) {
          font = data.font
        }
        let textAlign = 'center'
        if (data.hasOwnProperty('textAlign') && (typeof data.textAlign == 'string')) {
          textAlign = data.textAlign
        }

        let textBaseLine = 'middle'
        if (data.hasOwnProperty('textAlign') && (typeof data.textBaseLine == 'string')) {
          textBaseLine = data.textBaseLine
        }
        
        
        for(let i=0;i<wlen;i++){
          let wordInfo = wordsList[i]

            // 验证数据
            if (!checkWordsInfo(wordInfo)){
                console.error('fail to draw , some pramas of wordsList can not be empty')
                return 'fail'
             }
              
            // 设置具体渲染信息
            let mycolor = color
            if (wordInfo.hasOwnProperty('textColor') && (typeof wordInfo.textColor == 'string')) {
                mycolor = wordInfo.textColor
             }
            let myfont = font
            if (wordInfo.hasOwnProperty('font') && (typeof wordInfo.font == 'number')) {
                myfont = wordInfo.font
            }
            let myTextAlign = textAlign
            if (wordInfo.hasOwnProperty('textAlign') && (typeof wordInfo.textAlign == 'string')) {
                myTextAlign = wordInfo.textAlign
            }
            let myTextBaseLine = textBaseLine
            if (wordInfo.hasOwnProperty('textAlign') && (typeof wordInfo.textBaseLine == 'string')) {
                myTextBaseLine = wordInfo.textBaseLine
            }
              
            ctx.setFillStyle(mycolor)
            ctx.setFontSize(myfont)
            ctx.setTextAlign(myTextAlign)
            ctx.setTextBaseline(myTextBaseLine)

            // 开始绘制文字
            ctx.fillText(wordInfo.word, wordInfo.x, wordInfo.y)
        }
    }

4.生成图片

// 生成图片
  ctx.draw(true,function(){
    wx.canvasToTempFilePath({
      canvasId: mycanvasid,
      x:offsetx,
      y:offsety,
      width:width,
      height:height,
      destWidth:destWidth,
      destHeight:destHeight,
      success:function(res){
          
        // 绘制成功后,返回该图片的地址,并执行回调函数
        dosomething(res.tempFilePath)
      },
      fail: function () {
        // 导出图片错误
        wx.showModal({
          title: '导出图片时出错',
          content: '请重新尝试!',
        })
      },
      complete: function () {
        wx.hideLoading()
      }
    }, this)
  })

5.暴露接口

module.exports = {
  gk_draw: gk_draw
}

6.使用

在要用的地方引入,如

// pages/result/result.js
const draw = require('../../utils/draw.js')

// 在需要的地方绘制

 /**
   * 生成图片(接口版)
   */
  createImg:function(){

    let that = this
    let backgroundImg = '/images/result.jpg'
    let nickname = wx.getStorageSync('nickname')
    let imgurl = wx.getStorageSync('imgurl')

    let params = {
      canvasId:'share',
      textColor:'white',
      offsetx:0,
      offsety:0,
      width:750,
      height:1334,
      destWidth:750,
      destHeight:1334,
      imgList:[
        {
          source: backgroundImg,
          x:0,
          y:0,
          width:750,
          height:1334
        },
        {
          source: imgurl,
          x: 92,
          y: 80,
          width: 652,
          height: 690
        },
      ],
      wordsList:[
        {
          word:nickname,
          font:50,
          x: 375,
          y:1209
        }
      ]
    }

    draw.gk_draw(params,function(res){
      // 注,这里的res就是图片地址了
      that.setData({
        resultimg:res
      })
    })
  }
  

看看效果(截图的,打下广告)


22.png

完整版代码如下:

传送门至draw.js

上一篇下一篇

猜你喜欢

热点阅读