绘制分享海报要注意的点
发布于 2 年前 作者 qiangren 2256 次浏览 来自 分享

业务上经常存在一种场景就是需动态的绘制出一张漂亮的海报,并且能支持到把海报分享给朋友或者朋友圈的操作。今天就简单来聊聊怎么实现动态绘制海报并保存分享图片的功能。

首先,选用Qrcode插件来实现动态生成二维码,由于小程序的限制,这边实现直接通过把qrcode源码放到项目里,用自定义utils的方式来引入。

接下来就实现绘制方法:

Tips: 微信小程序canvas有两套API,大家按照自己的需求使用,本文使用的是老的接口;

<!-- 因为小程序同层渲染和原生组件的坑,用wxss设置样式使视窗不可见会导致绘制不出来,但是用内联样式可以实现 -->
  <canvas
    wx:if="{{!posterImage}}"
    class="poster-canvas"
    id="poster"
    canvas-id="poster"
    style="width:{{standardWidth}}px;height:{{standardHeight}}px;position:fixed;left:2000px;top:0;"
  />
  <!-- 仅仅用做于绘制qrcode载体,不需要显示 -->
  <canvas
    wx:if="{{!posterImage}}"
    id="qrcode"
    canvas-id="qrcode"
    class="qrcode-canvas"
    style="width:{{qrCodeWidth}}px;height:{{qrCodeHeight}}px;position:fixed;left:2000px;top:0;"
  />


用两个元素的原因是因为绘制过程中经常会出现位置偏移的问题,这边通过占位元素和定位来实现位置固定;

const { ctx } = await this.getCtx('poster')

// 获取canvas元素
getCtx (selector) {
  return new Promise((resolve) => {
    const ctx = wx.createCanvasContext(selector, this)
    const { standardWidth, standardHeight } = this.properties
    resolve({
      ctx,
      width: standardWidth,
      height: standardHeight,
    })
  })
}

// 绘制二维码
drawQrCode (canvasNodeRef) {
const { ctx } = canvasNodeRef
const { qrCode } = this.data.jsonData
const { standardRate, shareLink } = this.data
return new Promise(async (resolve, reject) => {
QrCode.api.draw(shareLink, 'qrcode', qrCode.width, qrCode.height, this)
// Qrcode.api.draw的坑,接口流程已经完成,但是实际上并没有绘制完成(视窗不可见),所以通过一些延迟来确保大部分都已经绘制完成
await this.delay()
wx.canvasToTempFilePath({
  canvasId: 'qrcode',
  success: async (res) => {
    ctx.drawImage(
      res.tempFilePath,
      qrCode.x * standardRate,
      qrCode.y * standardRate,
      qrCode.width * standardRate,
      qrCode.height * standardRate
    )
    resolve(res.tempFilePath)
  },
  fail: (err) => {
    reject(err)
  }
  }, this)
})
}


绘制的过程中需要注意处理一下绘制的图片资源

getTransImg (imgUrl) {
  const newUrl = imgUrl.replace('http://', 'https://')
  return new Promise((resolve) => {
    this.downLoadPicSource(newUrl).then(async (res) => {
      resolve(res.tempFilePath)
    }).catch(e => {
      console.log('资源下载失败: ', e.message, newUrl)
    })
  })
}

downLoadPicSource (url) {
  const newUrl = url.replace('http://', 'https://')
  return downloadSource({ url: newUrl })
}


这时候几本就把二维码绘制完成了,如果需要绘制其他元素可以自己按需添加,以下举例几种:

// 绘制背景
const bgUrl = await this.getTransImg(pictureUrl)
ctx.drawImage(bgUrl, 0, 0, standardWidth, standardHeight)

// 绘制logo/头像
const { ctx } = canvasNodeRef
const { logo } = this.data.jsonData
const { standardRate } = this.data
const logoUrl = await this.getTransImg(this.properties.logoUrl)
ctx.drawImage(
  logoUrl,
  logo.x * standardRate,
  logo.y * standardRate,
  logo.width * standardRate,
  logo.height * standardRate,
)


绘制完成之后需要保存海报图,需要注意保存图片失真的问题,需要根据当前系统的dpx做一些处理

ctx.draw(true, () => {
  // 绘制完成 - 把canvas转化为图片
  const dpr = wx.getSystemInfoSync().pixelRatio
  const picW = this.properties.standardWidth
  const picH = this.properties.standardHeight
  wx.canvasToTempFilePath({
    canvasId: 'poster',
    width: picW,
    height: picH,
    destWidth: picW * dpr,
    destHeight: picH * dpr,
    success: (res) => {
      this.setData({
        inDrawing: false,
        posterImage: res.tempFilePath,
      })
      this.triggerEvent('drawEnd', res.tempFilePath)
    },
    fail: (e) => {
      this.drawError('生成海报失败', e)
    }, this)
  }


绘制完成之后,只要把海报图回显到页面中即可。接下来就只要实现长按分享或者保存就可以实现了

// 判断分享图片功能是否可用
if (!wx.canIUse('showShareImageMenu')) {
  // 把图片保存到相册
  wx.saveImageToPhotosAlbum({
    filePath: this.data.posterImage,
    success: (evt) => {
      console.log('保存成功: ', evt)
      wx.showToast({
        title: '保存成功'
      })
    },
    fail: (err) => {
      console.log('保存失败了: ', err)
      wx.showToast({
        title: '保存失败',
        icon: 'none'
      })
    }
  })
  return
}
// 分享海报
wx.showShareImageMenu({
  path: this.data.posterImage,
  success: (res) => {
    console.log('分享成功', res)
    wx.showToast({
      title: '操作成功',
    })
  },
  fail: (err) => {
    console.log('分享失败', err)
    // 取消的情况不弹窗提醒
    if (err.errMsg.indexOf('cancel') !== -1) {
      return
    }
    wx.showToast({
      title: `分享失败:${err.errMsg}`,
      icon: 'none'
    })
  }
})


大功告成!

回到顶部