需求:html页面转换pdf,页面有多个模块,页面中有文本、echarts、表格等模块,一个模块占一页,因为模块高度不够,所以需要垂直居中
通过html2canvas和jspdf实现,html2canvas用于将页面元素生成canvas,jspdf用于将页面元素导出pdf
效果:
以下代码可以直接运行,背景图需要自行加一下
注意点:背景图片不支持跨域图片,非要使用跨域图片。可以通过js转成base64,使用base64设置背景图片
图片需要在服务器端才能正常导出,可以使用vscode的live-server插件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
http-equiv="X-UA-Compatible"
content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.1/echarts.min.js"></script>
<title>Document</title>
</head>
<style>
* {
margin: 0;
padding: 0;
}
.module1,
.module2 {
background-image: url('../bg.jpg');
background-repeat: no-repeat;
background-size: 100% 100%;
height: 100vh;
}
h1 {
text-align: center;
padding-top: 30vh;
margin-bottom: 10vh;
font-size: 100px;
letter-spacing: 10px;
font-weight: bold;
}
p {
text-align: center;
margin-top: 30px;
font-size: 50px;
font-weight: bold;
letter-spacing: 10px;
}
button {
display: block;
margin: 0 auto;
margin-top: 5vh;
width: 20vh;
height: 10vh;
outline: none;
}
.module2 h1 {
padding-top: 30px;
font-size: 80px;
margin-bottom: 40px;
}
.module2 p {
margin-top: 10px;
font-size: 40px;
}
.echarts {
width: 100%;
height: 70vh;
}
table {
table-layout: fixed;
word-break: break-all;
width: 99%;
margin: 0 auto;
}
table th {
padding: 10px 20px;
text-align: center;
}
table td {
padding: 10px 20px;
text-align: center;
}
.one {
width: 300px;
}
</style>
<body>
<div class="module1 module">
<h1>你好世界</h1>
<p>2月3日</p>
<button onclick="downloadPDf()">下载pdf</button>
</div>
<div class="module2 module">
<h1>echarts</h1>
<div class="echarts"></div>
</div>
<div class="module2 module">
<h1>文本</h1>
<p>
本网站上的图像不能用于以下目的。如有违反,将承担损害赔偿责任,敬请遵守。 1、不能用图像本身或处理过的图像的二次分发 2、不能用作服务标记或商标 3、不能使用违法、虚假、诽谤、侵权等违反公序良俗的内容。 4、不能用作色情图片 5、不能用于成人媒体、娱乐相关(包括歌舞俱乐部等)和约会服务
6、不能用于互联网异性恋介绍业务 7、不能用在消费金融中的应用 8、不能欺骗,例如使用人的照片通过各种软件渠道去骗取任性与钱财 9、不能用照片损坏国家形象和利益
</p>
</div>
<div class="module2 module">
<h1>表格</h1>
<table
border="1"
cellspacing="0"
cellpadding="0"
border="1">
<tr>
<th class="one">故障统计</th>
<th>1月27日</th>
<th>1月28日</th>
<th>1月29日</th>
<th>1月30日</th>
<th>1月31日</th>
<th>2月1日</th>
<th>2月2日</th>
<th>周累计</th>
</tr>
<tr>
<td class="one">风险</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td class="one">服务</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
</table>
</div>
</body>
<script>
const myChart = echarts.init(document.querySelector('.echarts'))
// 绘制图表
myChart.setOption({
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'],
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20],
},
],
})
const jsPDF = jspdf.jsPDF
const downloadPDf = async dom => {
// 对于一些不想显示在pdf上的元素,设置display:none,比如隐藏所有button
const buttons = document.querySelectorAll('button')
buttons.forEach(item => {
item.style.display = 'none'
})
// 获取所有需要生成pdf的模块
const modules = document.querySelectorAll('.module')
if (!modules.length) return
// a4纸固定宽高
const a4Width = 595.28
const a4Height = 841.89
const pdf = new jsPDF('p', 'pt')
// 生成所有pdf页
async function setPdfPage() {
for (let i = 0; i < modules.length; i++) {
const item = modules[i]
const canvas = await html2canvas(item)
const contentWidth = canvas.width
const contentHeight = canvas.height
const pageData = canvas.toDataURL('image/jpeg', 1) // 第二个参数为图片质量,1为最高质量
let imgHeight = (a4Width / contentWidth) * contentHeight // 根据a4纸比例,计算出图片的高度
const marginY = (a4Height - imgHeight) / 2 // 计算出图片的上下边距
// 第三个参数图片x轴位置,第四个参数图片y轴位置,第五个参数图片宽度,第六个参数图片高度
pdf.addImage(pageData, 'JPEG', 0, marginY > 0 ? marginY : 0, a4Width, imgHeight > a4Height ? a4Height : imgHeight)
// 最后一个模块不需要再新增空白页
if (i < modules.length - 1) {
pdf.addPage()
}
}
}
await setPdfPage()
pdf.save('TestReport.pdf') // 导出pdf
// 还原元素
buttons.forEach(item => {
item.style.display = 'block'
})
}
</script>
</html>