近期遇到这类需求:根据不同用户 不同 的选择,最后生成带有 个人用户名 和 不同标签 的海报图,方便在朋友圈裂变传播。一共 7 张海报,状态分别如下图1:
图1
如果要前端来生成这些图片,我们要怎么处理呢?继续往下看~
canvas.toDataURL() 生成图片
首先,前端要考虑怎么生成一张图片,这时候就会用到 canvas
的 toDataURL
方法。
canvas.toDataURL(type,encoderOptions)
方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为 96dpi。
确定前端可以做后,下一步就是绘制我们需要的内容了。
创建一个画布
从 图1 中可以看到,除了文案和车型图片不同,背景底图是一致的。我们可以拆分需求:
绘制底图
绘制文案
这就需要用到 canvas
了,按照需求,我们要生成的图片大小是 640*810
,先来看一下 html 代码。
<section class="mask-poster">
<!-- img 用来存储 canvas 生成的图片地址 -->
<img id="poster" class="save-poster" src="" />
<canvas id="canvas" width="640" height="810"></canvas>
</section>
我们需要先指定 canvas 画布的大小,然后开始 js 绘制。
js 创建画布如下:
var oCanvas = document.querySelector('#canvas');
var ctx = oCanvas.getContext('2d');
ctx.fillRect(0, 0, 640, 810); // 指定画布大小
这时候会创建出一个黑色的 宽640
, 高810
的画布。效果如下图:
绘制海报背景图 (canvas.drawImage)
提示:这里有坑,要注意以下两点:
需要 加载完成 的图片,再写入到 canvas
涉及到跨域的图片文件,图片需要设置
crossOrigin
属性为anonymous
;
// 绘制海报底图
var oImg = new Image();
oImg.setAttribute('crossOrigin', 'anonymous');
oImg.src = 'https://s.autoimg.cn/topic/heycar/img/res.png';
oImg.onload = function() {
ctx.drawImage(oImg, 0, 0, 640, 810);
}
绘制效果如下图:
绘制文字
以 顶部标题 为例,需求是这样的:
文案:用户昵称 的命中座驾是
文字居中,昵称超出显示 ‘...’
我们先绘制昵称:
// 绘制文字字体和大小
ctx.font = '40px "PingFang SC", Helvetica, Arial, "Hiragino Sans GB", "Microsoft Yahei", STHeiTi, sans-serif';
// 绘制文字颜色,fillStyle 默认值是 #000000,黑色的话可以不用写这行。
ctx.fillStyle = '#000000';
// 把文案绘制到 canvas 上,100 代表距离画布左上角 x 轴偏移量,60 代表 y 轴的偏移量
ctx.fillText('悟空悟空', 100, 60);
图 4
代码运行效果如上方图4(忽略图中的框和线),思考一下问题:
虚线框处内容需要根据前面文字的宽度,计算自己 x 轴偏移量。
两个文案拼接,怎么居中?
我们接着往下看。
计算文字宽度
ctx.measureText('悟空悟空').width;
measureText()
方法返回包含一个对象,该对象包含以像素计的指定字体宽度。
文字居中
ctx.textAlign = 'center';
注: textAlign
的值是以 fillText
的 x 值来对齐。
所有属性值:
值 | 描述 |
---|---|
start | 默认。文本在指定的位置开始。 |
end | 文本在指定的位置结束。 |
center | 文本的中心被放置在指定的位置。 |
left | 文本左对齐。 |
right | 文本右对齐。 |
回到绘制标题,步骤分别如下:
1. 以画布中心位置(320)为基准,昵称 ‘悟空悟空’ 设置 ctx.textAlign='end';
。另一半文案设置 ctx.textAlign='start';
2. 昵称 如 图4 中,以 220 为中心居中。 opts.x
代表 步骤1 中的基准数值(320), wordWidth
代表 measureText
计算出来的昵称宽度。
ctx.fillText('悟空悟空', opts.x + (wordWidth - 220) / 2, 60);
3. 文字超出规定宽度(320),显示 ‘...’。 opts.text
代表昵称, maxWidth
代表320
, opts.y
代表 y 轴偏移(60)。超出规定宽 截断 该昵称。
var wordWidth = 0; //记录文本宽度
for( var i = 0; i < opts.text.length; i++) {
wordWidth += ctx.measureText(opts.text[i]).width; // 计算文本宽度
// 文字超出规定宽,显示 "...""
if(wordWidth > maxWidth) {
ctx.fillText(opts.text.substring(0,i) + '...', opts.x + (wordWidth - 220) / 2, opts.y);
break;
}
}
标题最终版效果如下图:
其它文案
绘制原理同上,这里不多做介绍。
结语:完整版 demo 可以点击左下角 阅读原文 查看。大家往后遇到类似业务,希望这篇文章能帮助到你。如果你有更好的方案,欢迎留言分享,THANKS。