首页 > 编程语言 >小程序使用 canvas 制作活动分享海报的一点点细节

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

时间:2022-11-28 11:08:47浏览次数:43  
标签:canvas testLine 海报 一点点 let context line arrText

前言

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

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

大致效果预览:

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

方案评估

基本功能

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

两类方案

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

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

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

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

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

思维导图

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

值得分享的技术点解析

初始化 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,可自动给低版本用户提示更新微信以使用当前小程序。

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

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

设计稿的建议

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

总结

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

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

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

标签:canvas,testLine,海报,一点点,let,context,line,arrText
From: https://blog.51cto.com/u_15680916/5890707

相关文章