为了账号安全,请及时绑定邮箱和手机立即绑定

小程序的canvas绘图的封装

标签:
WebApp

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

1.关于canvas的一些坑

  • 1.canvas组件在hidden情况下依然可以执行绘制操作,并且不占据空间。

  • 2.canvas组件在hidden情况下需要指定width和height,在hidden情况下,无法对canvas的width和height进行动态调整

  • 3.要想动态调整canvas的width和height,需要使用wx:if="{{}}"来配合。 // 缺点是,绘制的时候canvas是显示的
    注:也就是使用控制条件,平时不显示,在需要绘制的时候才显示进行绘制图片(可以放到页面底部)

2.canvas的封装

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

2.1 制定目标

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

2.2 敲定参数

  • 1.canvasId  // string类型

  • 2.offsetx   // number类型。也就是wx.canvasToTempFile()中的x,这个x不一定是0,所以我们设为这个参数名。

  • 3.offsety   // number类型。同上

  • 4.width     // number类型。wx.canvasToTempFile()中的width

  • 5.height    // number类型。wx.canvasToTempFile()中的height

  • 6.destWidth   // number类型。wx.canvasToTempFile()中的destWidth

  • 7.destHeight  //number类型。wx.canvasToTempFile()中的destHeight

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

  • 8.imgList  // array类型

  • 9.wordsList  // array类型

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

  • 8.imgList   // array类型

    • 1.source   // 图片地址

    • 2.x        // 绘制的起点位置的x轴

    • 3.y        // 绘制的起点位置的y轴

    • 4.width    // 绘制的width

    • 5.height   // 绘制的height

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

  • 9.wordsList   // array类型

    • 1.word    // 即需要绘制的问题

    • 2.x       // 绘制的位置点x

    • 3.y       // 绘制的位置点y

    • 同时可能还需要设置其他信息,如下(非必须)

    • 4.font    // 要绘制的文字大小size,用于setFontSize()

    • 5.textAlign   // 文字的水平对齐方式,仅为 'left' 'center' 'right'

    • 6.textBaseLine  // 文字的纵向对齐方式,仅为 'top' 'bottom' 'middle' 'normal'

    • 7.textColor    // 文字的颜色

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

  • 9.wordsList   // array 类型

  • 10.font    // 全局的font,不传入时,默认为35

  • 11.textAlign  // 全局的textAlign ,不传入时,默认为center

  • 12.textBaseLine  // 全局的textBaseLin,不传入时,默认为middle

  • 13.textColor     // 全局的textColor,不传入时,默认为 black

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

2.3函数封装

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

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

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

  • 1.分别是验证{}第一层的数据,包含canvasId,offsetx,offsety,width...等

  • 2.如果imgList存在,验证其必填的数据是否填完

  • 3.同理,验证wordsList中必须要填的数据

// 验证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.jsconst 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
      })
    })
  }



作者:甚时跃马归来
链接:https://www.jianshu.com/p/882ae6b7326b


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消