一、因项目需求,在手机web中动态生成一张如下图片:
二、html结构如下:
==>
body {
margin: 0;
}
canvas {
background-color: #f0f0f0;
}
>
===========>
隐藏域中的内容已经由PHP输出,可供使用,下面分析图片生成过程。
获取到兼容过后的 requestAnimationFrame 方法,该方法类似于setTimeout/setInterval,都是通过递归调用同一方法来达到不断渲染画面的效果,但requestAnimationFrame是专门为浏览器提供动画支持的API,当页面处于非激活状态下时动画暂停渲染,节省CPU开销。
window
.requestAnimationFrame = window
.requestAnimationFrame || window
.mozRequestAnimationFrame || window
.webkitRequestAnimation || window
.msRequestAnimationFrame;
画布初始化,获取画布宽度,设置字体等等
var canvas = document.getElementById(
'canvas');
var ctx = canvas.getContext(
'2d');
var ctxWidth = canvas.width;
//画布宽度
var summaryWidth =
480;
//简介宽度
ctx.save();
ctx.font =
'34px 微软雅黑';
ctx.fillStyle =
'#000';
标题、简介、线条、二维码围栏设置
//标题设置
var title = document.getElementById(
'title').value;
var titleWidth = ctx.measureText(title).width;
//文字宽度
ctx.fillText(title, (ctxWidth - titleWidth)/
2,
470);
ctx.restore();
//简介设置
ctx.save();
ctx.font =
'20px 微软雅黑';
ctx.fillStyle =
'#969696';
var summary = document.getElementById(
'summary').value;
var summaryArr = getLineText(summary, summaryWidth);
ctx.fillText(summaryArr[
0], (ctxWidth - summaryWidth)/
2,
530);
ctx.fillText(summaryArr[
1], (ctxWidth - summaryWidth)/
2,
560);
ctx.fillText(summaryArr[
2], (ctxWidth - summaryWidth)/
2,
590);
ctx.restore();
//线条
ctx.save();
ctx.translate(/
ctx.strokeStyle =
'#c2c2c2';
ctx.lineWidth =
1;
ctx.moveTo(
80,
610);
ctx.lineTo(
550,
610);
ctx.stroke();
ctx.restore();
//二维码围栏设置
ctx.save();
ctx.beginPath();
ctx.lineWidth =
5;
ctx.moveTo(
172,
688);
ctx.lineTo(
172,
666);
ctx.lineTo(
194,
666);
ctx.stroke();
ctx.restore();
ctx.save();
ctx.beginPath();
ctx.lineWidth =
5;
ctx.moveTo(
469,
688);
ctx.lineTo(
469,
666);
ctx.lineTo(
447,
666);
ctx.stroke();
ctx.restore();
ctx.save();
ctx.beginPath();
ctx.lineWidth =
5;
ctx.moveTo(
172,
945);
ctx.lineTo(
172,
967);
ctx.lineTo(
194,
967);
ctx.stroke();
ctx.restore();
ctx.save();
ctx.beginPath();
ctx.lineWidth =
5;
ctx.moveTo(
469,
945);
ctx.lineTo(
469,
967);
ctx.lineTo(
447,
967);
ctx.stroke();
ctx.restore();
剖析以上涉及到的相关知识点:
measureText()方法返回一个对象,该对象包含以像素为单位的指定字体的宽度
由于店铺名简短,因此直接一行输出
考虑到店铺简介文字较长,且需求中最多只显示三行,最后一行最后两个字替换为省略号显示,因此编写方法getLineText(),以获取指定行文字。
线条以及二维码围栏则根据设置好的坐标进行相应绘制。
{
var lines = [];
var lastIndex =
0;
for(
var i =
0, l = text.length; i < l; ++i){
var subStr = text.slice(lastIndex, i);
if(ctx.measureText(subStr).width > maxWidth){
lines.push(text.slice(lastIndex, i -
1));
lastIndex = i;
}
}
if(lines.length ==
0){
lines.push(text);
}
else{
lines.push(text.slice(lastIndex));
}+
if(lines.length >=
3){
lines = lines.slice(
0,
3);
//最后两个文字使用...替代
var lastItem = lines[lines.length -
1];
var posLastTwo = lastItem.lastIndexOf(lastItem[lastItem.length -
2]);
lastItem = lastItem.substring(
0, posLastTwo) +
'...'
lines[lines.length -
1] = lastItem;
}
return lines;
}
绘制顶部背景,logo以及二维码
//画布背景
ctx.globalCompositeOperation = headerReady =
false;
var header =
new Image();
header.src = document.getElementById(
'header').value;
header.onload =
function(){
headerReady =
true;
}
//portrait头像
var protraitReady =
false;
var portrait =
new Image();
portrait.src = document.getElementById(
'portrait').value;
portrait.onload =
function(){
protraitReady =
true;
}
//二维码
var qrcodeReady =
false;
var qrcode =
new Image();
qrcode.src = document.getElementById(
'qrcode').value;
qrcode.onload =
function(){
qrcodeReady =
true;
};
(headerReady){
ctx.drawImage(header,
0,
0);
//由于顶部背景logo处为透明,因此先画背景后画logo
if(protraitReady){
ctx.drawImage(portrait,
224,
94,
200,
200);
}
}
if(qrcodeReady){
ctx.drawImage(qrcode, (ctxWidth -
274)/
2,
680,
274,
274);
}
}
var stop =
null;
var main =
function(){
render();
stop = window.requestAnimationFrame(main);
if(headerReady && protraitReady && qrcodeReady){
var img = canvas.toDataURL();
//转换成64位图片,该操作需在服务器中方可正常执行,可考虑构建一个本地服务器。
window.cancelAnimationFrame(stop);
//取消渲染
}
}
main();
至此一张完整的图片便通过canvas画成了
素材如下