之前写过两个海报插件,但都是基于后端实现,比较吃服务器资源,实际使用起来不尽如人意,于是重新写了这个js版本,在客户端浏览器就解决了海报生成。
市面上已经有的有很多,但是看起来还不错的真少。
此版本除了在实现上有所变化,在实际的效果上也做了一些优化。
效果预览
js版本在于轻量级,所以使用起来也很简单,在heade前引入以下Javascript 。
核心代码
class NiceposterGenerator {
constructor() {
this.canvas = null;
this.context = null;
this.createBackground();
}
createBackground() {
const canvas = document.createElement("canvas");
canvas.id = "myCanvas";
canvas.width = 760;
canvas.height = 1150;
this.canvas = canvas;
this.context = this.canvas.getContext("2d");
}
drawRoundedImage(context, image, x, y, width, height, radius) {
context.save();
context.beginPath();
context.moveTo(x + radius, y);
context.lineTo(x + width - radius, y);
context.quadraticCurveTo(x + width, y, x + width, y + radius);
context.lineTo(x + width, y + height - radius);
context.quadraticCurveTo(
x + width,
y + height,
x + width - radius,
y + height
);
context.lineTo(x + radius, y + height);
context.quadraticCurveTo(x, y + height, x, y + height - radius);
context.lineTo(x, y + radius);
context.quadraticCurveTo(x, y, x + radius, y);
context.closePath();
context.clip();
context.drawImage(image, x, y, width, height);
context.restore();
}
truncateChineseString(str, maxLength){
var len = 0;
var result = '';
for (var i = 0; i < str.length; i++) {
var charCode = str.charCodeAt(i);
if (charCode >= 0x4E00 && charCode <= 0x9FFF) {
len += 2;
} else {
len += 1;
}
if (len > maxLength) {
break;
}
result += str.charAt(i);
}
return result;
}
generate(meta){
meta.font = meta.font ? meta.font : "SimSun";
var title = this.truncateChineseString(meta.title,28);
var desc = this.truncateChineseString(meta.desc,150);
var authorName = this.truncateChineseString(meta.authorName,30);
var context = this.context;
context.fillStyle = "white";
context.fillRect(0, 0, this.canvas.width, this.canvas.height);
var img = new Image();
img.crossOrigin = "Anonymous";
img.src = meta.background;
img.onload = () => {
var imgCanvas = document.createElement("canvas");
var imgContext = imgCanvas.getContext("2d");
imgCanvas.width = this.canvas.width - 100;
imgCanvas.height = meta.backgroundHeight;
var cornerRadius = 40;
this.drawRoundedImage(
imgContext,
img,
0,
0,
imgCanvas.width,
imgCanvas.height,
cornerRadius
);
context.drawImage(imgCanvas, 50, 50);
var date = new Date();
var day = date.getDate();
var month = date.getMonth() + 1;
var year = date.getFullYear();
context.font = "300 150px " + meta.font;
context.fillStyle = "white";
context.fillText(day, 90, 250);
context.font = "36px " + meta.font;
context.fillStyle = "white";
context.fillText(month + "/" + year, 115, 320);
context.font = "40px " + meta.font;
context.fillStyle = "white";
context.fillText(title, 100, 650 - 60);
context.font = "30px " + meta.font;
context.fillStyle = "white";
context.fillText(authorName, 180, 840);
var cardWidth = this.canvas.width - 100;
var cardHeight = 180;
var cardX = 50;
var cardY = 930;
context.save();
context.beginPath();
context.moveTo(cardX + cornerRadius, cardY);
context.lineTo(cardX + cardWidth - cornerRadius, cardY);
context.quadraticCurveTo(
cardX + cardWidth,
cardY,
cardX + cardWidth,
cardY + cornerRadius
);
context.lineTo(cardX + cardWidth, cardY + cardHeight - cornerRadius);
context.quadraticCurveTo(
cardX + cardWidth,
cardY + cardHeight,
cardX + cardWidth - cornerRadius,
cardY + cardHeight
);
context.lineTo(cardX + cornerRadius, cardY + cardHeight);
context.quadraticCurveTo(
cardX,
cardY + cardHeight,
cardX,
cardY + cardHeight - cornerRadius
);
context.lineTo(cardX, cardY + cornerRadius);
context.quadraticCurveTo(cardX, cardY, cardX + cornerRadius, cardY);
context.closePath();
context.fillStyle = "rgb(230, 230,230)";
context.fill();
context.restore();
var circularImg = new Image();
circularImg.crossOrigin = "Anonymous";
circularImg.src = meta.authorAvatar;
circularImg.onload = function () {
var circleDiameter = meta.avatarWidth;
var circleX = 100;
var circleY = 800;
var circleRadius = circleDiameter / 2;
context.save();
context.beginPath();
context.arc(
circleX + circleRadius,
circleY + circleRadius,
circleRadius,
0,
2 * Math.PI
);
context.closePath();
context.clip();
context.drawImage(
circularImg,
circleX,
circleY,
circleDiameter,
circleDiameter
);
context.restore();
};
context.font = "20px " + meta.font;
context.fillStyle = "black";
context.fillText(meta.footer, 100, 980);
context.font = "18px " + meta.font;
context.fillStyle = "rgb(120,120,120)";
context.fillText(meta.slogen, 100, 1060);
var qrCode = new QRCode(document.createElement("div"), {
text: meta.targetQrcodeUrl,
width: 120,
height: 120
});
var qrCodeImage = qrCode._el.children[0].toDataURL("image/png");
var qr_x = this.canvas.width - 100 - 120;
var qr_y = cardY + 30;
var qrCodeImgElement = new Image();
qrCodeImgElement.src = qrCodeImage;
qrCodeImgElement.onload = () => {
cornerRadius = 20;
context.save();
context.beginPath();
context.moveTo(qr_x + cornerRadius, qr_y);
context.lineTo(qr_x + 125 - cornerRadius, qr_y);
context.quadraticCurveTo(
qr_x + 125,
qr_y,
qr_x + 125,
qr_y + cornerRadius
);
context.lineTo(qr_x + 125, qr_y + 125 - cornerRadius);
context.quadraticCurveTo(
qr_x + 125,
qr_y + 125,
qr_x + 125 - cornerRadius,
qr_y + 125
);
context.lineTo(qr_x + cornerRadius, qr_y + 125);
context.quadraticCurveTo(
qr_x,
qr_y + 125,
qr_x,
qr_y + 125 - cornerRadius
);
context.lineTo(qr_x, qr_y + cornerRadius);
context.quadraticCurveTo(qr_x, qr_y, qr_x + cornerRadius, qr_y);
context.closePath();
context.fillStyle = "white";
context.fill();
context.restore();
this.drawRoundedImage(
context,
qrCodeImgElement,
qr_x,
qr_y,
125,
125,
cornerRadius
);
};
var chineseText = desc+'...';
var lines = [];
var words = chineseText.split("");
var currentLine = words[0];
var lineHeight = 40;
var maxWidth = 560;
context.font = "20px " + meta.font;
context.fillStyle = "white";
var x = 100;
var y = 720 - 60;
for (var i = 1; i < words.length; i++) {
var word = words[i];
var testLine = currentLine + word;
var metrics = context.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > maxWidth) {
lines.push(currentLine);
currentLine = word;
} else {
currentLine = testLine;
}
}
lines.push(currentLine);
for (var j = 0; j < lines.length; j++) {
context.fillText(lines[j], x, y);
y += lineHeight;
}
};
}
}
使用
引入qrcode库
/qrcode.min.js
Javascript
//
const meta =
{
id:1111,
title:"通用文章海报Javascript插件",
desc:"iPoster是一款通用的文章精美海报生成的javascript插件...",
authorName:"LYLARES",
authorAvatar:"https://www.lylares.com/wp-content/uploads/2017/02/30845bc72dceb15689126805931d636c-2.png",
footer:"LYLARES BLOG",
slogen:"望眼相看,似是故人来。",
font:"iconfont",
avatarWidth:60,
background:"https://www.lylares.com/wp-content/uploads/2017/02/dbc4b303c0ca21c10d6bdb4a4ecb7616.jpeg",
backgroundHeight:850,
targetQrcodeUrl:"https://www.todaybing.com/detail/YLsUhviX.html",
posterWidth:760,
posterHeight:1150
};
iposterGenerator.generate(meta);
var src = iposterGenerator.canvas.toDataURL("image/png");//生成的海报图片
参数说明
id:暂时无用
title:自定义标题
desc:自定义描述,会自动截断字符不超过三行。
authorName:作者名称
authorAvatar:非必需项,不为空将在左下角展示作者头像,位置固定,受到背景图高度影响
footer:footer
slogen:slogen
font:字体,默认与网页一致
avatarWidth:作者头像宽度
background:背景图地址
backgroundHeight:背景图高度
targetQrcodeUrl:二维码目标地址
posterWidth:海报宽
posterHeight:海报高
背景图高度会影响整个海报的观感,下个版本将加入自动调整。
以前的后端版本可以参考之前的文章:
shareposter-WordPress文章海报生成插件
shareposter是一款为wordpress文章自动生成分享海报的插件
shareposter是一款能自动给文章生成一张分辨率为750*1366海报的插件,海报样式精美且自带文章链接 ...
https://www.lylares.com/shareposter.html转载原创文章请注明,转载自: LYLARES BLOG » Iposter v2.0 通用文章海报插件纯Javascript 实现