小程序使用 canvas 制作活动分享海报的一点点细节

简介: 小程序使用 canvas 制作活动分享海报的一点点细节

前言


活动海报制作,在 C 端是很常见的功能,前端程序员应该或多或少的接触过,h5 写的话方案成熟,可以用原生 canvas api 写,也可以使用成熟的插件如 html-to-imagehtml2canvas 等; 不过在微信小程序中开发这个功能,第一次的话就会遇到挺多坑的,api 的兼容性、凌乱的文档都在暗示我们过程没那么简单;

如果你也在做相关的功能,这篇博客可能会有所帮助!


大致效果预览:


方案评估


基本功能


在当前活动中点击按钮可生成一张活动海报,可保存到相册或者直接微信转发,海报中的小程序码和分享人有映射关系,可以记录谁分享的。


两类方案


这种需求在营销活动中其实很常见,常见的方案有两类:

一是后端生成、前端展示,好处是兼容性良好,不需要关注设备和平台之间的差异性;不过稍微复杂点的UI, 后端的工作量剧增,画起来太费劲。

二是前端生成,后端只提供关键数据,由前端通过 canvas 等技术来制作图片,具体又分纯 canvas 绘制和插件编译 Dom 等方案如(html2canvas)。

具体采用哪种方案也要看具体的情况了,网上也有不少讨论,针对小程序环境,html2canvas 是不支持的,所以如果要使用插件,还需要额外的工作,比如将代码运行在 webview 中,最后我还是决定使用 小程序提供的 canvas api 来绘制海报。


1687782106762.png


思维导图


1687782098074.png


值得分享的技术点解析


初始化 canvas 环境


我们使用最新type="2d"模式,性能和写法上更接近原生

对应文档


<canvas type="2d" id="canvas2d" class="canvasTag"></canvas>


注意:官方文档例子是写在小程序页面里的,模式为 page, 如果是写在小程序组件里 Component, 需要使用 in(this) 来固定 this 对象, 否则 res 获取不到。


示例:
const query = wx.createSelectorQuery().in(this)
query.select('#canvas2d')
    .fields({ node: true, size: true })
    .exec(async res => {
const canvas = res[ 0 ].node


canvas 尺寸和生成图片的关系


因为canvas 是以像素来绘制的,不像 svg 那样可以无损,所以为了在高分屏设备上保持清晰度,需要根据当前设备的 dpr 来对canvas 尺寸进行放大,保持比例,这样可以消除图片的锯齿感。


示例:
const canvas = res[ 0 ].node
canvas.width = windowWidth * dpr
canvas.height = windowHeight * dpr
const ctx = canvas.getContext('2d')
ctx.scale(dpr, dpr)


文字的换行计算


canvas 绘制不像 DOM 可以自适应位置,所以需要根据文字数量动态计算行数和占用到高度


formatText (context, text = '' , x, y, maxWidth, lineHeight) {
  let arrText = text.split('')
  let line = ''
  let row = 1
  for (let n = 0; n < arrText.length; n++) {
    let testLine = line + arrText[n]
    let metrics = context.measureText(testLine)
    let testWidth = metrics.width
    if (testWidth > maxWidth && n > 0) {
      // 超出 2 行缩略,显示 ...
      if (row === 2) {
        for (let n = 0; n < line.length; n++) {
          // eslint-disable-next-line no-undef, no-new-wrappers
          let $testLine = new String(line).substring(0, line.length - 1 - n) + '...'
          let $metrics = context.measureText($testLine)
          let $testWidth = $metrics.width
          if ($testWidth <= maxWidth) {
            context.fillText($testLine, x, y)
            return
          }
        }
      } else {
        context.fillText(line, x, y)
      }
      line = arrText[n]
      y += lineHeight
      row++
    } else {
      line = testLine
    }
  }
  context.fillText(line, x, y)
}


获取指定文本的高度


// 获取文本高度
getFormatTextHeight (context, text = '', maxWidth, lineHeight) {
  let arrText = text.split('')
  let line = ''
  let row = 1
  for (let n = 0; n < arrText.length; n++) {
    if (row === 2) {
      break
    }
    let testLine = line + arrText[n]
    let metrics = context.measureText(testLine)
    let testWidth = metrics.width
    if (testWidth > maxWidth && n > 0) {
      line = arrText[n]
      row++
    } else {
      line = testLine
    }
  }
  return row * lineHeight
}


Base64 图片的展示


Base64 类型的图片不能直接渲染到画布上,需要先缓存下来转为本地路径, 主要用到小程序提供的 wx.getFileSystemManager() 和 writeFile 两个 api。


// base64 转本地文件
setBase64Save (base64File) {
  const fsm = wx.getFileSystemManager()
  // eslint-disable-next-line no-useless-escape
  let extName = base64File.match(/data:\S+/(\S+);/)
  if (extName) {
    extName = extName[1]
  }
  let fileName = Date.now() + '.' + extName
  return new Promise((resolve, reject) => {
    let filePath = wx.env.USER_DATA_PATH + '/' + fileName
    fsm.writeFile({
      filePath,
      data: base64File.replace(/^data:\S+/\S+;base64,/, ''),
      encoding: 'base64',
      success: res => {
        resolve(filePath)
      },
      fail () {
        // eslint-disable-next-line prefer-promise-reject-errors
        reject('写入失败')
      }
    })
  })
}


网络图片的绘制


社区说网络图片不能直接绘制,需要转化为本地路径 path(但是当这样操作后,发现真机调试时候并没有成功,反而使用直接绘制(drawImage)的方式成功了。)


wx.getImageInfo({
  src: headerImg,
  success (res) {
    headerImgObj.src = res.path || ''
    headerImgObj.onload = resolve
    headerImgObj.onerror = reject
  }
})


版本兼容


实测发现,开发者工具中的内核版本和真机并不完全一致,所以建议要在真机上调试下,否则可能在上线后出现不同的兼容性问题;比如 wx.drawImage 这个 api 实测在基础库版本 2.25.0 之上才能正常使用,可以在微信管理后台中配置最低使用版本 2.25.2,可自动给低版本用户提示更新微信以使用当前小程序。


1687782075561.png


1687782083516.png


设计稿的建议


建议以标准尺寸设计稿如 750px 来绘制画布,然后通过缩放来展示。


总结


小程序的 canvas 标签和原生的有较大差异,需要结合文档和社区反馈,多做调试,api 经常变动也需要关注,如果某个接口废弃了,要注意在下个迭代发布时做好处理,因为只要发布,之前的接口就失效了;

以上就是我在做小程序海报时候觉得有价值、值得分享的点,希望对大家有帮助。

目录
相关文章
|
7月前
|
前端开发 小程序 JavaScript
微信小程序canvas手写签字
微信小程序canvas手写签字
92 0
|
7月前
|
小程序 前端开发 API
微信小程序保存海报的过程
微信小程序保存海报的过程
113 0
|
7月前
|
前端开发 小程序
微信小程序canvas画布绘制base64图片并保存图片到相册中
微信小程序canvas画布绘制base64图片并保存图片到相册中
211 0
|
2月前
|
前端开发 小程序 JavaScript
小程序海报,极简的实现方式
小程序海报,极简的实现方式
39 2
小程序海报,极简的实现方式
|
2月前
|
前端开发 小程序 JavaScript
小程序 canvas 生成海报 一次搞掂
小程序 canvas 生成海报 一次搞掂
29 1
|
2月前
|
前端开发 小程序 JavaScript
微信小程序 canvas 备忘
微信小程序 canvas 备忘
38 0
|
5月前
|
运维 小程序 前端开发
好的商业模式-----小程序定制资料,加一张好看的海报,在推广中就可以找到用户中了,云服务部署收5000,部署是一种服务,定制化,游戏开发创者,仲裁劳务会剪视频好,提供服务,想增加一些新功能收费,会说
好的商业模式-----小程序定制资料,加一张好看的海报,在推广中就可以找到用户中了,云服务部署收5000,部署是一种服务,定制化,游戏开发创者,仲裁劳务会剪视频好,提供服务,想增加一些新功能收费,会说
|
5月前
|
前端开发 小程序
【微信小程序-原生开发】实用教程20 - 生成海报(实战范例为生成活动海报,内含生成指定页面的小程序二维码,保存图片到手机,canvas 系列教程)
【微信小程序-原生开发】实用教程20 - 生成海报(实战范例为生成活动海报,内含生成指定页面的小程序二维码,保存图片到手机,canvas 系列教程)
420 0
|
7月前
|
前端开发 小程序
微信小程序绘制canvas时在不同 设备上的大小不同的问题
微信小程序绘制canvas时在不同 设备上的大小不同的问题
265 0
|
7月前
|
前端开发 小程序
【微信小程序5】利用canvas实现纯色背景抠图功能
【微信小程序5】利用canvas实现纯色背景抠图功能
338 0