h5 canvas 实现海报图片效果
前几天想用纯前端实现一下海报的效果,做出一张海报图出来不需要使用ps这些工具,能给用户下载的一张图,忙着赶项目,代码写得比较杂乱,望大佬指导,话不多说直接上代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.poster {
width: 400px;
border: solid 1px black;
}
</style>
</head>
<body>
<div class="poster">
</div>
<canvas class="qrcode"></canvas>
</script>
<script src="qrcode.js"></script>
<script src="index.js"></script>
</body>
</html>
这里使用了自适应海报的宽度会随着poster的实际宽度来定义,然后二维码模块使用了qrcode这个库
var posterContent = {
/**
* 海报的封面
* url为海报图片的链接地址
* text为内部文字的属性,context:内容,x:当前内容的x,y为当前内容的y,size:为字体大小,color:字体颜色
* text为数组可以有多个字体
* 下面的同上
*/
titlePage: {
url: 'https://book-upload-1251142715.file.myqcloud.com/6c1ab843-cd80-48d8-923b-e838f06fefeb/1.jpg',
text: [{
context: '海报封面',
x: 10,
y: 100,
size: 10,
color: '#aaa'
}]
},
/**
* 只针对title可以设置他的初始位置和缩放倍率
* @type {startX:初始x,startY:初始y,imgPower:title的放大倍率默认为 0~1}
* center是否水平居中
*/
title: {
url: 'https://baige-static-1251142715.file.myqcloud.com/book.baige.me/images/baige_100_100.png',
imgPower: 0.3,
startX: 0,
startY: 10,
center: true,
text: [{
context: 'logo文字',
x: 10,
y: 10,
size: 20,
color: '#ccc'
}]
},
urlcode: {
/**
* url链接的地址
* imgPower二维码的缩放倍率
* startX二维码默认的开始位置;不设置的话默认上下左右居中,也可以自己微微调试
* startY二维码默认开始的y位置
*/
url: 'http://you.baige.me/view/Xho',
imgPower: 0.8,
startX: 0,
startY: 0,
text: [{
context: '二维码',
x: 10,
y: 10,
size: 30,
color: 'black'
}]
},
option: {
/**
* color为分割线的颜色
* top分割线离上封面的距离
* bottom分割线离下的距离
* slitLineSize分割线的长度,默认为宽度的一半,不建议修改
* 挂载元素
*/
color: "#ccc",
top: 20,
bottom: 20,
splitLineSize: 100,
el: ".poster"
}
}
//调用此函数传入数据模板
drawPoster(posterContent);
function drawPoster(posterContent) {
if (document.querySelector(posterContent.option.el) == null) {
console.log("跳出执行")
return;
}
getImgSize(posterContent, function (img) {
drawImage(posterContent, img, function (data) {
var imgDom = document.createElement('img');
imgDom.src = data;
document.querySelector(posterContent.option.el).appendChild(imgDom);
});
})
/**
* 获取图片的宽高
* @param url 图片的路径
*/
function getImgSize(posterContent, callback) {
if (Object.keys(posterContent).length != 4) {
console.log("传入图片不符合格式")
return;
}
var images = {
titlePage: {width: 0, height: 0},
title: {width: 0, height: 0}
};
var sum = 0;
Object.keys(posterContent).forEach(function (key, index) {
if (index >= 2) return;
var image = new Image();
image.src = posterContent[key].url;
// 图片先加载完,才可以得到图片宽度和高度
image.onload = function () {
let width = image.width;
let height = image.height;
sum++;
if (index == 0) {
images.titlePage.width = parseInt(width);
images.titlePage.height = parseInt(height);
if (sum == 2) {
callback(images);
}
}
if (index == 1) {
images.title.width = parseInt(width);
images.title.height = parseInt(height);
if (sum == 2) {
callback(images);
}
}
}
})
}
/**
* 合并图片
* @param images 图片路径数组
* @param callback
*/
function drawImage(contentItems, img, callback) {
var posterWidth = document.querySelector(contentItems.option.el).clientWidth,
canvas = document.createElement('canvas'), // 创建canvas元素
titlePageProPortion = img.titlePage.width / img.titlePage.height,
titleProPortion = img.title.width / img.title.height;
canvas.width = posterWidth; // canvas宽度
var splictH = posterWidth / 2;
if (contentItems.option.splitLineSize != null) {
splictH = contentItems.option.splitLineSize
}
canvas.height = posterWidth / titlePageProPortion + splictH + contentItems.option.top + contentItems.option.bottom; // canvas的高度
var context = canvas.getContext('2d'); // 创建渲染
// 设置背景为白色
context.fillStyle = '#fff';
context.fillRect(0, 0, canvas.width, canvas.height);
var posterW, posterH;
var imageItem_1 = new Image();
imageItem_1.src = contentItems.titlePage.url;
// 跨域
imageItem_1.crossOrigin = 'Anonymous';
var xAxis_1 = 0;
var yAxis_1 = 0;
var istitleOk = false;
// 图片加载成功,绘制图片
imageItem_1.onload = function () {
posterW = posterWidth;
posterH = posterWidth / titlePageProPortion;
context.drawImage(imageItem_1, xAxis_1, yAxis_1, posterW, posterH);
var imageItem_2 = new Image();
if (contentItems.titlePage.text.length > 0) {
contentItems.titlePage.text.forEach(function (item, index) {
//console.log("这里开始添加了文字", item.color, item.size, item.context)
context.fillStyle = item.color;
context.font = 'bold ' + item.size + 'px Arial';
context.fillText(item.context, xAxis_1 + item.x, yAxis_1 + item.y);
})
}
imageItem_2.src = contentItems.title.url;
// 跨域
imageItem_2.crossOrigin = 'Anonymous';
var xAxis_2 = 0;
var yAxis_2 = posterH + 20;
if (contentItems.title.center) {
xAxis_2 = posterWidth / 4 - posterWidth / 2 * contentItems.title.imgPower / 2;
}
context.moveTo(posterW / 2, posterH + contentItems.option.top);
//设置起点状态
var splitLineSize = posterWidth / 2;
if (contentItems.option.splitLineSize != null) {
splitLineSize = contentItems.option.splitLineSize;
}
context.lineTo(posterW / 2, posterH + splitLineSize + contentItems.option.top);
//设置末端状态
context.lineWidth = 1;
//设置线宽状态
context.strokeStyle = contentItems.color;
//设置线的颜色状态
context.stroke();
//进行绘制
// 图片加载成功,绘制图片
imageItem_2.onload = function () {
istitleOk = true;
context.drawImage(imageItem_2, xAxis_2 + contentItems.title.startX, yAxis_2 + contentItems.title.startY, posterWidth / 2 * contentItems.title.imgPower, (posterWidth / 2) / titleProPortion * contentItems.title.imgPower);
if (contentItems.title.text.length > 0) {
contentItems.title.text.forEach(function (item, index) {
context.fillStyle = item.color;
context.font = 'bold ' + item.size + 'px Arial';
context.fillText(item.context, xAxis_2 + item.x, yAxis_2 + item.y);
})
}
}
imageItem_2.onerror = function () {
console.log("第二张图片解析失败")
}
//二维码生成的地方
var qrcodeDom = document.createElement('canvas');// 创建canvas元素
document.querySelector("body").appendChild(qrcodeDom);
var qrcode = new QRCode(qrcodeDom, {
text: contentItems.urlcode.url,
width: 128,
height: 128,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H
});
var qrcodeUrl = '';
var time = setInterval(function () {
qrcodeUrl = qrcodeDom.childNodes[1].src;
if (qrcodeUrl != '' && istitleOk) {
clearInterval(time)
var imageItem_3 = new Image();
imageItem_3.src = qrcodeUrl;
// 跨域
imageItem_3.crossOrigin = 'Anonymous';
var splitLineS = posterW / 4;
if (contentItems.option.splitLineSize != null) {
splitLineS = contentItems.option.splitLineSize / 2
}
var xAxis_3 = posterW / 2 + 50 + contentItems.urlcode.startX;
var yAxis_3 = posterH + 20 + splitLineS - (100 * contentItems.urlcode.imgPower) / 2 + contentItems.urlcode.startY;
// 图片加载成功,绘制图片
imageItem_3.onload = function () {
context.drawImage(imageItem_3, xAxis_3, yAxis_3, 100 * contentItems.urlcode.imgPower, 100 * contentItems.urlcode.imgPower);
if (contentItems.urlcode.text.length > 0) {
contentItems.urlcode.text.forEach(function (item, index) {
context.fillStyle = item.color;
context.font = 'bold ' + item.size + 'px Arial';
context.fillText(item.context, xAxis_3 + item.x, yAxis_3 + item.y);
})
}
callback(canvas.toDataURL('image/jpeg', 1))
}
imageItem_3.onerror = function () {
console.log("第二张图片解析失败")
}
}
}, 50)
//二维码的数据
}
imageItem_1.onerror = function () {
console.log("第一张图片路径解析失败")
}
}
}
这里封装了一个drawPoster的方法,我们需要提前定义一个对象进去对象的格式在最前面,大家可以看一下,由于时间关系所以并没有完善其他功能,最后给大家看一下效果图
这里所形成的海报图片文字这些就是一整张图片了,大家有兴趣可以试一下。