前言
海报分享功能在许多应用中应该是很常见的,因为它作为一种常用的应用推广和拉新的方式。
接下来看个实际的案例,如下:
把任务拆解下:
- 如何绘制海报
- 如何把绘制后的海报保存到相册
绘制海报
用 canvas
来绘制海报。 这里需要了解基本的 canvas api
,不熟悉可以先去了解下相关 Canvas API
定义 canvas
元素
<template> <view class="poster-container"> <canvas class="poster" canvas-id="posterId"></canvas> <button class="btn" @click="onSave">保存至相册</button> </view> </template>
获取 canvas
上下文对象
const context = uni.createCanvasContext('posterId');
绘制背景图片
图片支持远程图片和本地图片,网络图片要通过 getImageInfo
/ downloadFile
先下载。
context.drawImage('/static/poster.png', 0, 0, 320, 410); // 绘制背景图片
绘制头像和昵称
const avatarLeft = 185; const avatarTop = 18; const avatarWidth = 16; const avatarHeight = 16; context.drawImage( '/static/avatar.png', avatarLeft, avatarTop, avatarWidth, avatarHeight );
从设计上来看头像和昵称是对齐的。
直接按照设计稿距离来设定看看效果
const nickName = '墙头草中的顶尖的'; const nameLeft = avatarLeft + avatarWidth + 7; context.setFillStyle('#ffffff'); context.setFontSize(12); context.fillText(nickName, nameLeft, 21, 96);
实际效果如下:
发现与预期效果对应不上,思考下这是为什么? 文字设置距离偏移的参考基线不是文字的顶部。
怎么验证我的说法呢?可以 x
值设置为 0
。
const nickName = '墙头草中的顶尖的'; const nameLeft = avatarLeft + avatarWidth + 7; context.setFillStyle('#ffffff'); context.setFontSize(12); context.fillText(nickName, nameLeft, 0, 96); // 修改为 0
再看看效果:
发现参考点几乎是文字的底部,这就验证了上面的结论。
怎么解决这个问题呢?
- 获取文本行的高度
- 更改偏移的参考点
通常知道文本行实际占用的高度,需要知道其 line-height
,上面并不知道其 line-height
值。canvas
本身并不直接支持line-height
属性,显然没办法获得相对准确的行高。
只能采用第二种方式,从 canvas
提供 API 来看,可以更改文本相行对齐的点。
const nickName = '墙头草中的顶尖的'; const nameLeft = avatarLeft + avatarWidth + 7; context.setFillStyle('#ffffff'); context.setFontSize(12); context.setTextBaseline('top'); // 更改基线对齐点 context.fillText(nickName, nameLeft, 21, 96);
再来看看效果如下:
接下来实现二维码区域,主要白色背景 + 二维码 + 文案
白色背景区域
const rectWidth = 300; const rectHeight = 89; const rectLeft = 10; const rectTop = 311; context.setFillStyle('#ffffff'); context.fillRect(rectLeft, rectTop, rectWidth, rectHeight);
效果如下:
绘制二维码
const qrcodeLeft = 20; const qrcodeTop = rectTop + 10; const qrcodeWidth = 68; const qrcodeHeight = 68; context.drawImage( '/static/qrcode.png', qrcodeLeft, qrcodeTop, qrcodeWidth, qrcodeHeight );
二维码这里直接采用现成图片。实际上前端可以通过 weapp.qrcode.esm.js
在前端生成二维码,再把它绘制上去。
绘制多行文本
const startX = qrcodeLeft + qrcodeWidth + 15; const text1 = '与志同道合的,他们一起成长'; context.setFillStyle('#161413'); context.setFontSize(14); context.setTextBaseline('top'); context.fillText(text1, startX, rectTop + 29); const text2 = '扫码即可进入“Get一下”社区'; context.font = 'bold 14px Arial'; context.fillText(text2, startX, rectTop + 51);
看看最后的效果:
左上角的部分没有绘制,思路同头像和昵称一样。 接下来只要把图片保存到相册即可。
保存到相册
在小程序中,提供方法支持如下:
const onSave = () => { // 转换为临时路径 uni.canvasToTempFilePath({ canvasId: 'posterId', success: (res) => { // 保存图片到相册 uni.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success: () => { uni.showToast({ title: '保存成功', icon: 'none', }); }, fail: () => { uni.showToast({ title: '保存失败', icon: 'none', }); }, }); }, }); };
这里基本上把一个海报绘制
扩展
在绘制名称时,用户名称长短不一至,如果名字过长时会出现什么效果呢
const nickName = '墙头草中的顶尖的墙头草中的顶尖的'; const nameLeft = avatarLeft + avatarWidth + 7; context.setFillStyle('#ffffff'); context.setFontSize(12); context.fillText(nickName, nameLeft, 21, 96);
从效果来看,发现文字直接重叠。如果希望超出的部分能够通过省略号来省略,是可以的
const nickName = '墙头草中的顶尖的墙头草中的顶尖的'; const nameLeft = avatarLeft + avatarWidth + 7; context.setFillStyle('#ffffff'); context.setFontSize(12); context.fillText(nickName, nameLeft, 21, 96); let text = ''; const textArr = nickName.split(''); const ellipsisWidth = context.measureText('...').width; // 省略号的宽度 for (let i = 0; i < textArr.length; i++) { const temp = text + textArr[i]; const metrics = context.measureText(temp); if (metrics.width + ellipsisWidth > 96) { text = text + '...'; break; } text = temp; } context.fillText(text, nameLeft, 21, 96);
上面主要是通过 measureText
方法获得文字对应宽度,针对超出的部分采用省略号替代。当然,如果希望完整地显示名字,也可以使用换行的方式。具体的实现方式,就留给大家自己思考和实现了。
处理后效果如下:
头像实现圆角
默认采用圆角头像,如果用户上传的头像没有进行裁剪处理,导致图片出现非圆角情况,那么在海报上呈现的效果可能会有所差异。
先使用一个非圆角的图片,代码修改如下:
const avatarLeft = 185; const avatarTop = 18; const avatarWidth = 16; const avatarHeight = 16; context.drawImage( '/static/avatar-rect.png', avatarLeft, avatarTop, avatarWidth, avatarHeight );
效果如下:
现在对图片处理一下:
const avatarLeft = 185; const avatarTop = 18; const avatarWidth = 16; const avatarHeight = 16; context.beginPath(); context.arc( avatarLeft + avatarWidth / 2, avatarTop + avatarHeight / 2, avatarWidth / 2, 0, 2 * Math.PI ); context.closePath(); context.clip(); // 绘制圆形头像 context.drawImage( '/static/avatar-rect.png', avatarLeft, avatarTop, avatarWidth, avatarHeight );
效果如下:
圆角是实现了,发现其他区域内容都被裁剪了。 这是为什么? clip()
改变了绘画环境。ctx()
调用后,所裁剪的区域就是 clip ()
后的绘制环境,clip()
之后的绘画只能在裁剪区域中渲染,不能访问画布上的其他区域。
怎么处理这个问题呢 ? 通过 save()
和 restore()
.
context.save(); // 暂存 context.beginPath(); context.arc( avatarLeft + avatarWidth / 2, avatarTop + avatarHeight / 2, avatarWidth / 2, 0, 2 * Math.PI ); context.closePath(); context.clip(); // 绘制圆形头像 context.drawImage( '/static/avatar-rect.png', avatarLeft, avatarTop, avatarWidth, avatarHeight ); context.restore(); // 恢复
效果如下:
总结
- 通过
canvas
+ 小程序提供 API 实现海报绘制、保存海报到相册 - 优化特殊情况下名称和头像的展示方式。
以上就是微信小程序海报绘制示例讲解的详细内容,更多关于小程序海报绘制的资料请关注其它相关文章!