接到的需求
需要能够将页面上已有的dom转为pdf并下载,能够满足单个下载和批量下载
实现思路
- 将dom转为图片
- 图片转为pdf
- 单个下载直接下载
- 多个下载添加到压缩包下载
代码实现
将dom转换为图片
我这里使用的是html2canvas
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script>
// 这里就可以拿到生成的canvas图片
const canvas = await html2canvas(document.querySelector('.fm-img'))
const pageData = canvas.toDataURL('image/png');
</script>
图片转为pdf
// pdf用的jspdf
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.3.1/jspdf.umd.min.js"></script>
<script>
let A4_WIDTH = 595;
let A4_HEIGHT = 842;
const pdf = new jspdf.jsPDF("", "pt", "a4");
let contentWidth = canvas.width; // 生成的canvas图片整体宽度
let contentHeight = canvas.height; // 生成的canvas图片整体高度
let position = 0; // 开始的位置
let imgWidth = A4_WIDTH - 12 // -12为了页面有右边距
let imgHeight = (A4_WIDTH / contentWidth) * contentHeight
// 单页情况处理
// pageData是上面拿到的png内容、 JPEG 图片格式、
// 6 为添加到的pdfx轴的位置、40是y轴的位置,距离上边距、imgWidth图片宽度、imgHeight渲染的图片高度
pdf.addImage(pageData, 'JPEG', 6, 40, imgWidth, imgHeight);
pdf.save('文件名.pdf')
</script>
多页批量导出
<!DOCTYPE HTML>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
<script src="https://code.jquery.com/jquery-3.6.4.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.3.1/jspdf.umd.min.js"></script>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.3.5/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/jszip/3.4.0/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.0/FileSaver.min.js"></script>
</head>
<body>
<!-- 封面 -->
<div><img style="height:842;width: 595px" src="./fm.png" class='fm-img' /></div>
<div><img style="height:842;width: 595px" src="./fg.png" class='fg-img' /></div>
<div class="content">
<h1>春日里的温柔时光</h1>
<p>
春风轻拂,万物复苏,大地披上了一袭嫩绿的新装,仿佛一夜之间,世界被温柔地唤醒。在这个充满生机的季节里,我独自漫步于乡间小径,享受着春日里独有的温柔时光。
</p>
<p>
阳光透过稀疏的云层,洒下斑驳陆离的光影,给这静谧的田野增添了几分暖意。远处,一片片金黄色的油菜花海随风起伏,宛如金色的波浪,散发着淡淡的清香,吸引着蜜蜂和蝴蝶在花间穿梭,它们忙碌的身影,似乎在为这春日的画卷添上灵动的一笔。
</p>
<p>
脚下的泥土松软而湿润,每一步都踏出了春天的声音。小草从土里探出头来,好奇地打量着这个世界,它们虽不起眼,却以顽强的生命力,铺就了一条绿色的地毯,让人不忍践踏。偶尔,几朵野花点缀其间,红的、黄的、蓝的……它们虽不名贵,却以独特的姿态,展现着春天的多彩与活力。
</p>
<p>
沿着小路前行,一条清澈见底的小溪映入眼帘。溪水潺潺,发出悦耳的声响,像是大自然最悠扬的乐章。溪边,柳树依依,嫩绿的枝条轻拂水面,仿佛少女在梳洗着秀发。我不由自主地停下脚步,坐在溪边的石头上,闭上眼睛,让心灵随着这清澈的流水一同流淌,感受那份宁静与和谐。
</p>
<p>
此时此刻,我仿佛与世隔绝,所有的烦恼与忧愁都随风而去,只留下心灵的纯净与宁静。我深深地吸了一口气,空气中弥漫着泥土与花草的混合气息,那是春天的味道,是生命的味道,让人心旷神怡,陶醉不已。
</p>
<p>
春日里的温柔时光,是如此的短暂而珍贵。它让我们感受到了大自然的恩赐,也让我们体会到了生活的美好与希望。让我们珍惜这份温柔,用心去感受每一个春天的到来,用爱去呵护这个美好的世界。
</p>
</div>
<div class="content">
<h1>春日里的温柔时光</h1>
<p>
春风轻拂,万物复苏,大地披上了一袭嫩绿的新装,仿佛一夜之间,世界被温柔地唤醒。在这个充满生机的季节里,我独自漫步于乡间小径,享受着春日里独有的温柔时光。
</p>
<p>
阳光透过稀疏的云层,洒下斑驳陆离的光影,给这静谧的田野增添了几分暖意。远处,一片片金黄色的油菜花海随风起伏,宛如金色的波浪,散发着淡淡的清香,吸引着蜜蜂和蝴蝶在花间穿梭,它们忙碌的身影,似乎在为这春日的画卷添上灵动的一笔。
</p>
<p>
脚下的泥土松软而湿润,每一步都踏出了春天的声音。小草从土里探出头来,好奇地打量着这个世界,它们虽不起眼,却以顽强的生命力,铺就了一条绿色的地毯,让人不忍践踏。偶尔,几朵野花点缀其间,红的、黄的、蓝的……它们虽不名贵,却以独特的姿态,展现着春天的多彩与活力。
</p>
<p>
沿着小路前行,一条清澈见底的小溪映入眼帘。溪水潺潺,发出悦耳的声响,像是大自然最悠扬的乐章。溪边,柳树依依,嫩绿的枝条轻拂水面,仿佛少女在梳洗着秀发。我不由自主地停下脚步,坐在溪边的石头上,闭上眼睛,让心灵随着这清澈的流水一同流淌,感受那份宁静与和谐。
</p>
<p>
此时此刻,我仿佛与世隔绝,所有的烦恼与忧愁都随风而去,只留下心灵的纯净与宁静。我深深地吸了一口气,空气中弥漫着泥土与花草的混合气息,那是春天的味道,是生命的味道,让人心旷神怡,陶醉不已。
</p>
<p>
春日里的温柔时光,是如此的短暂而珍贵。它让我们感受到了大自然的恩赐,也让我们体会到了生活的美好与希望。让我们珍惜这份温柔,用心去感受每一个春天的到来,用爱去呵护这个美好的世界。
</p>
</div>
<div class="content">
<h1>春日里的温柔时光</h1>
<p>
春风轻拂,万物复苏,大地披上了一袭嫩绿的新装,仿佛一夜之间,世界被温柔地唤醒。在这个充满生机的季节里,我独自漫步于乡间小径,享受着春日里独有的温柔时光。
</p>
<p>
阳光透过稀疏的云层,洒下斑驳陆离的光影,给这静谧的田野增添了几分暖意。远处,一片片金黄色的油菜花海随风起伏,宛如金色的波浪,散发着淡淡的清香,吸引着蜜蜂和蝴蝶在花间穿梭,它们忙碌的身影,似乎在为这春日的画卷添上灵动的一笔。
</p>
<p>
脚下的泥土松软而湿润,每一步都踏出了春天的声音。小草从土里探出头来,好奇地打量着这个世界,它们虽不起眼,却以顽强的生命力,铺就了一条绿色的地毯,让人不忍践踏。偶尔,几朵野花点缀其间,红的、黄的、蓝的……它们虽不名贵,却以独特的姿态,展现着春天的多彩与活力。
</p>
<p>
沿着小路前行,一条清澈见底的小溪映入眼帘。溪水潺潺,发出悦耳的声响,像是大自然最悠扬的乐章。溪边,柳树依依,嫩绿的枝条轻拂水面,仿佛少女在梳洗着秀发。我不由自主地停下脚步,坐在溪边的石头上,闭上眼睛,让心灵随着这清澈的流水一同流淌,感受那份宁静与和谐。
</p>
<p>
此时此刻,我仿佛与世隔绝,所有的烦恼与忧愁都随风而去,只留下心灵的纯净与宁静。我深深地吸了一口气,空气中弥漫着泥土与花草的混合气息,那是春天的味道,是生命的味道,让人心旷神怡,陶醉不已。
</p>
<p>
春日里的温柔时光,是如此的短暂而珍贵。它让我们感受到了大自然的恩赐,也让我们体会到了生活的美好与希望。让我们珍惜这份温柔,用心去感受每一个春天的到来,用爱去呵护这个美好的世界。
</p>
</div>
<script>
$(document).ready(function() {
// 当前没下载的内容
let downloadArr = [];
function html2pdf() {
let zip = new JSZip();
let taskArr = [];
var elements = document.querySelectorAll('.content')
for (let i = 0; i < elements.length; i++) {
downloadArr.push({
isDownload: false,
ele: elements[i],
index: i
})
}
handleDown(zip)
}
function handleDown(zip) {
let list = [];
// 这里是限制每次处理的个数
// 实践下来发现每次限制一个可能比较好
const limit = 2;
downloadArr.forEach(item => {
if(!item.isDownload && (list.length < limit)) list.push(item)
})
const listIsOk = {isOk: false}
const cancelFn = () => {listIsOk.isOk=true}
const arr = list.map(item => {
const type = $(item.ele).data('type');
// 将所有节点创建异步任务
return getDocImg(item, type, listIsOk)
})
Promise.race(arr).then((res) => {
cancelFn()
const {elementItem, pdf} = res;
if(pdf) {
let datauri = pdf.output('dataurlstring');
let base64 = datauri.substring(28);
// 处理每个下载名,需要你自己改
zip.file(`Hello${elementItem.index}.pdf`, base64, {base64: true});
elementItem.isDownload=true
}
const isSuccess = checkIsSuccess();
if(!isSuccess) {
handleDown(zip)
} else {
handleZipSave(zip)
}
})
}
function handleZipSave(zip) {
zip.generateAsync({type:"blob"}).then(content => {
// 下载名称
// loading 结束
saveAs(content, "download.zip");
}).catch(err => {
console.log(err)
})
}
function checkIsSuccess() {
return downloadArr.every(item => !!item.isDownload)
}
let A4_WIDTH = 595;
let A4_HEIGHT = 842;
// listIsOk 是打算处理多个,然后选择其中一个处理快的,优先返回,然后中断其他promise
async function getDocImg(elementItem, type, listIsOk) {
const {ele:element, index} = elementItem;
const pdf = new jspdf.jsPDF("", "pt", "a4");
// type为判断封面,有则添加封面
if(type) {
const res1 = await html2canvas(document.querySelector('.fm-img'))
const pageData1 = res1.toDataURL('image/png');
if(listIsOk.isOk) return
pdf.addImage(pageData1, 'JPEG',0, 0, A4_WIDTH, A4_HEIGHT);
pdf.addPage()
}
const canvas = await html2canvas(element)
const pageData = canvas.toDataURL('image/png');
let contentWidth = canvas.width
let contentHeight = canvas.height
debugger
let pageHeightN = (contentWidth / A4_WIDTH) * A4_HEIGHT;
let leftHeight = contentHeight
let position = 0;
let imgWidth = A4_WIDTH - 12 // -10为了页面有右边距
let imgHeight = (A4_WIDTH / contentWidth) * contentHeight
if (leftHeight <pageHeightN) {
if(listIsOk.isOk) return
pdf.addImage(pageData, 'JPEG', 6, 40, imgWidth, imgHeight);
} else { // 分页
while (leftHeight > 0 && !listIsOk.isOk) {
if(listIsOk.isOk) return
pdf.addImage(pageData, "JPEG", 6, position + 40, imgWidth, imgHeight)
leftHeight -= pageHeightN
position -= A4_HEIGHT
// 避免添加空白页
if (leftHeight > 0) {
pdf.addPage()
}
}
if(type) {
if(listIsOk.isOk) return
pdf.addPage()
const res1 = await html2canvas(document.querySelector('.fg-img'))
const pageData1 = res1.toDataURL('image/png');
pdf.addImage(pageData1, 'JPEG',0,0, A4_WIDTH, A4_HEIGHT);
}
}
return {pdf,elementItem}
}
// 调用函数
html2pdf();
});
</script>
</body>
</html>
目前存在的问题
- 批量下载太多,会很慢(有想到过用workjs解决,但是work进程无法传入dom节点作为参数,所以就放弃了)
- 数量过多会导致浏览器崩溃,因为zip这个变量多大,占用内容过多
- pdf截取图片问题,无法识别文字,会将文字截断.
上面的问题暂时没想到其他解决方案,建议一次性处理少量dom节点