概述
在开发中,经常会有绘制海报的需求,再得贴个二维码什么的。接下来就和大家一起探讨下前端如何通过canvas来绘制海报。
绘制海报在我看来就是组合信息,首先你要对 canvas 有所了解,如果不会,建议先去看看文档。海报绘制的内容通常包括如下信息:
- 海报封面
- 用户信息
- 二维码
如下所示:
通常后台会返回海报封面图、用户信息以及二维码跳转链接给你,这里你要做的事情就两个:
- 将二维码跳转链接生成二维码
- canvas 绘制海报
那开始行动吧。
方案
在绘制过程中,如果引用的是在线图片资源可能会遇到图片跨域问题,并且需要注意的是,canvas.toDataURL() 方法对于在线图片或者说svg等资源的支持度是不友好的。海报的绘制我并没有铺满整个屏幕,这样可能会导致图片被拉伸,我们应该拿到封面图之后获取封面图的尺寸然后根据比例绘制,所以一般绘制海报的思路大致是这样的:
- 请求分享相关数据
- 将图片资源转成base64
- 跳转链接通过 Qrcode.js 生成二维码
- 计算海报元素尺寸位置信息
- 创建画布绘制
- 将画布导出并渲染
核心
图片转base64
刚刚有提到,canvas对在线资源的支持并不是特别友好,这里主要体现在当我们需要通过 canvas.toDataURL() 导出图片的时候会出现问题,canvas认为这会污染画布,所以通常来说,我会将在线图片转成base64,这里封装了转base64的方法,可直接使用,如下所示:
function base64(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function() {
if (this.status === 200) {
let blob = this.response
let fileReader = new FileReader()
fileReader.onloadend = function(e) {
let result = e.target.result;
resolve(result);
}
fileReader.readAsDataURL(blob)
}
}
xhr.onerror = function() {
reject();
}
xhr.send();
})
}
生成二维码
二维码生成这里给大家分享两种方法:
链接生成
链接生成我一般用在直接将跳转链接生成二维码并展示的情况,语法形式特别简单,如下所示:
const qrcodeSrc = 'http://qr.liantu.com/api.php?text=' + encodeURIComponent(url)
其中url为扫描二维码跳转的链接。
Qrcode.js
「1. 安装」
npm install --save qrcode
yarn add --save qrcode
「2. 导入」
import QRCode from 'qrcode'
「3. 使用」
QRCode.toDataURL(h5Url).then(qrCodeSrc => {
const qrCodeImg = new Image();
qrCodeImg.src = qrCodeSrc;
qrCodeImg.onload = function() {
// 将二维码绘制在海报上
}
}).catch(err => {
console.log(err)
})
比例计算
canvas绘制海报时最令我头疼的就是尺寸的计算啦,不过要是你掌握了一定的规律之后就很好实现了,刚刚我也有讲到,通常我在绘制海报的时候并不会让海报铺满整个屏幕,而是根据海报原图的尺寸信息结合设计图的比例关系进行绘制,避免拉伸,适应宽度即可。
所以你需要获取海报封面图的尺寸,并将其设置成canvas的尺寸,如下所示:
// 创建canvas
const oCanvas = document.createElement('canvas');
const context = oCanvas.getContext('2d');
// 将海报转成base64
this.base64(posterUrl).then(posterSrc => {
const posterImg = new Image();
posterImg.setAttribute('crossOrigin', 'anonymous');
posterImg.src = posterSrc;
posterImg.onload = () => {
// 获取图片尺寸
const w = posterImg.width;
const h = posterImg.height
// 根据图片尺寸设置canvas尺寸
oCanvas.width = w;
oCanvas.height = h;
// 绘制
context.drawImage(posterImg, 0, 0, w, h);
}
})
一般来说=讲,封面是最好画的,铺满canvas即可,不用做任何处理,头疼的主要是子元素的绘制。 首先我们需要计算原图与设计图的比例:
const ratio = w / 375
其中,w 为获取到的海报宽度,375 为设计稿的宽度,一般都为375px,根据实际情况定。计算出比例之后,其他元素就比较好处理啦,如下图:
设计稿宽度为375px,二维码的尺寸为218x218,二维码距离屏幕左上角的位置分别是79px/161px,得到这些信息之后我们就可以根据比例去绘制二维码啦,如下所示:
QRCode.toDataURL(h5Url).then(qrCodeSrc => {
const qrCodeImg = new Image();
qrCodeImg.src = qrCodeSrc;
qrCodeImg.onload = () => {
// 比例 = 图片元素宽度/设计图宽度
const ratio = w / 375;
// 间距 = 设计图上的间距 * 比例
const spacingX = 79 * ratio;
const spacingY = 161 * ratio;
// 尺寸 = 设计图上的尺寸 * 比例
const qrSize = 218 * ratio;
// 绘制
context.drawImage(qrCodeImg, spacingX, spacingY, qrSize, qrSize);
}
}).catch(err => {
console.log(err)
})
只要掌握了根据比例计算位置尺寸的技巧,绘制海报变得如此简单,这里大家可以举一反三。海报的其他部分也根据此方式来绘制。
实现
参照如下代码:
import QRCode from 'qrcode'
{
methods: {
draw() {
// 解构后台返回的海报封面图、跳转链接、用户头像
const { posterUrl, jumpUrl, avatar } = this.data;
// 创建canvas
const oCanvas = document.createElement('canvas');
const context = oCanvas.getContext('2d');
// 将图片资源转成base64
Promise.all([this.base64(posterUrl), this.base64(avatar)]).then(res => {
// 获取图片base64
const posterSrc = res[0];
const avatarSrc = res[1];
// # 加载底图
const posterImg = new Image();
posterImg.src = posterSrc;
posterImg.onload = () => {
// 获取图片尺寸
const w = posterImg.width;
const h = posterImg.height
// 根据图片尺寸设置canvas尺寸
oCanvas.width = w;
oCanvas.height = h;
// 获取canvas上下文
context.drawImage(posterImg, 0, 0, w, h);
// # 绘制二维码 - 放置在右下角
QRCode.toDataURL(jumpUrl).then(qrCodeSrc => {
const qrCodeImg = new Image();
qrCodeImg.src = qrCodeSrc;
qrCodeImg.onload = () => {
// 比例 = 图片元素宽度/设计图宽度
const ratio = w / 375;
// 间距 = 设计图上的间距 * 比例(右侧间距)
const spacing = 8 * ratio;
// 尺寸 = 设计图上的尺寸 * 比例
const qrSize = 62 * ratio;
// 计算x、y值
const x = w - qrSize - spacing;
const y = h - qrSize - spacing;
// 绘制
context.drawImage(qrCodeImg, x, y, qrSize, qrSize);
// # 绘制头像 - 头像在二维码的正中间
const oAvatar = new Image();
oAvatar.src = avatarSrc;
oAvatar.onload = () => {
const avatarSize = 12;
const _x = x + qrSize / 2 - avatarSize/2 * ratio;
const _y = y + qrSize / 2 - avatarSize/2 * ratio;
context.drawImage(oAvatar, _x, _y, avatarSize * ratio, avatarSize * ratio);
// 导出
this.dataURL = oCanvas.toDataURL();
}
}
}).catch(err => {
console.log(err)
})
}
}).catch(err => {
console.log(err);
})
}
}
}
❝上述代码为vue实现,仅供参考。
❞