//
解决了 pdf分页上一页与下一页内容错开样式不好看问题
//
ele:需要截图的元素节点对象 document.querySelector('#pdfDom')
//
pdfFileName:下载的文件名称
const PdfLoader = (ele, pdfFileName) =>
{
ele.style.fontFamily
= '宋体'
;
ele.style.padding
= '0 20px'
;
//
预留一定的时间给dom页面渲染完成 (如果你能保证dom已经渲染完成了包括图片 才去进行下面转化操作那就可以不用这个延迟器)
setTimeout(() =>
{
html2Canvas(ele, {
//
dpi: 300, // 清晰度
scale: 1,
//
将Canvas放大倍数 可以获得更具清晰的图片内容
//
!!!注意如果你生成的元素内容非常多是一个非常长列表 建议scale不要写太高或者删除这个属性 ,因为html2Canvas会吧内容转成
//
base64 会有一定的内容上限 最终返回没有base64编码(目前我尝试过生成55页的PDF,估计上限在70-100页)
useCORS:
true
,
//
是否允许跨域
allowTaint:
false
,
height: ele.offsetHeight,
width: ele.offsetWidth,
windowWidth: document.body.scrollWidth,
windowHeight: document.body.scrollHeight,
}).then(canvas
=>
{
//
未生成pdf的html页面高度
var
leftHeight =
canvas.height;
var
a4Width = 595.28
var
a4Height = 841.89
//
一页pdf显示html页面生成的canvas高度;
var
a4HeightRef = Math.floor(canvas.width / a4Width *
a4Height);
//
pdf页面偏移
var
position = 0
;
var
pageData = canvas.toDataURL('image/jpeg', 1.0);
//
生成的base64 如果你只是要图片 到这里就可以拿到base64图片编码(可以查一下base64转二进制 使用new FormData对象传给后端到服务器)
var
pdf =
new
JsPDF('x', 'pt', 'a4');
//
生成A4内容大小的pdf每页 更多参数配置可以看看下面的网站
//
https://blog.csdn.net/weixin_42333548/article/details/107630706
var
index = 1
,
canvas1
= document.createElement('canvas'
),
height;
pdf.setDisplayMode(
'fullwidth', 'continuous', 'FullScreen'
);
//
处理 pdf 上一页 与 下一页内容之间交叉不好看的断点样式
//
并且把内容转成二进制 生成pdf文件
function
createImpl(canvas) {
if
(leftHeight > 0
) {
index
++
;
var
checkCount = 0
;
if
(leftHeight >
a4HeightRef) {
var
i = position +
a4HeightRef;
for
(i = position + a4HeightRef; i >= position; i--
) {
var
isWrite =
true
for
(
var
j = 0; j < canvas.width; j++
) {
var
c = canvas.getContext('2d').getImageData(j, i, 1, 1
).data
if
(c[0] != 0xff || c[1] != 0xff || c[2] != 0xff
) {
isWrite
=
false
break
if
(isWrite) {
checkCount
++
if
(checkCount >= 10
) {
break
}
else
{
checkCount
= 0
height
= Math.round(i - position) ||
Math.min(leftHeight, a4HeightRef);
if
(height <= 0
) {
height
=
a4HeightRef;
}
else
{
height
=
leftHeight;
canvas1.width
=
canvas.width;
canvas1.height
=
height;
var
ctx = canvas1.getContext('2d'
);
ctx.drawImage(canvas,
0, position, canvas.width, height, 0, 0
, canvas
.width, height);
if
(position != 0
) {
pdf.addPage();
pdf.addImage(canvas1.toDataURL(
'image/jpeg', 1.0), 'JPEG', 0, 0
, a4Width,
a4Width
/
canvas1.width
*
height);
leftHeight
-=
height;
position
+=
height;
if
(leftHeight > 0
) {
//
给pdf文件 添加全屏水印
//
const base64 = ''; // 吧你要添加的水印内容搞成一张小图片 然后手动去转成base64编码 放在这里就可以了
//
for (let i = 0; i < 6; i++) {
//
for (let j = 0; j < 5; j++) {
//
const left = (j * 120) + 20;
//
pdf.addImage(base64, 'JPEG', left, i * 150, 20, 30);
//
};
//
};
pdf.addImage(pageData, 'JPEG', 0, i * 150, 20, 30
);
setTimeout(createImpl,
500
, canvas);
}
else
{
pdfSave()
//
当内容未超过pdf一页显示的范围,无需分页
if
(leftHeight <
a4HeightRef) {
pdf.addImage(pageData,
'JPEG', 0, 0, a4Width, a4Width / canvas.width *
leftHeight);
pdfSave()
}
else
{
try
{
pdf.deletePage(
0
);
setTimeout(createImpl,
500
, canvas);
}
catch
(err) {
console.log(err);
function
pdfSave() {
//
pdf文件生成完毕 自动下载到客户本地
pdf.save(pdfFileName + '.pdf'
)
setTimeout(()
=>
{
//
吧pdf文件上传到服务器
let base64 = pdf.output("datauristring"
);
let file
= convertBase64ToFile(base64, "报告"
);
let formData
=
new
FormData();
formData.append(
"file"
, file);
//
直接post 接口吧formData对象传给后端接口去让后端去获取就可以了
}, 1000
)
},
500
)
const convertBase64ToFile
= (urlData, filename) =>
{
var
arr = urlData.split('base64,'
);
var
type = arr[0].match(/:(.*?);/)[1
];
var
fileExt = type.split('/')[1
];
var
bstr = atob(arr[1
]);
var
n =
bstr.length;
var
u8arr =
new
Uint8Array(n);
while
(n--
) {
u8arr[n]
=
bstr.charCodeAt(n);
return
new
File([u8arr], filename + "." +
fileExt, {
type: type
export
default
PdfLoader;
3.页面使用
import PdfLoader from './pdf.js';
PdfLoader(document.getElementById('pdfDom'),'测试文件')
注意:如果你要生成的元素内容有图片 一定要把图片转成base64编码 在页面上去展示不然 在生成pdf或者canvas的时候会跨域,
如果你们的图片地址在腾讯云或者阿里上这种第三方 然后在使用地址转base64遇到了跨域问题 去找后端解决 让他们去配置白名单关闭跨域,(你可以尝试使用百度图库网络地址转base64不会遇到跨域问题)。
// 图片地址转base64
// url:https:xxxxx
const getBase64Image = function(url) {
function newImg(url) {
return new Promise((resolve, reject) => {
let image = new Image();
image.crossOrigin = 'Anonymous';
image.src = url;
image.onload = function() {
resolve(image);
return newImg(url).then((img) => {
let canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
let ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase();
let dataURL = canvas.toDataURL("image/" + ext);
return dataURL;
需求建议:如果你是通过条件查询后端返回数据 来动态生成这个元素 又不想让用户看到你渲染元素的过程,可以像我一样做个一个全局的遮罩弹窗并且展示下载进度 可以有一个更好的交互体验 执行完成后全部统一关闭。
注意:如果你生成的图片就是一个60-100页的pdf内容 想解决分页上限的导致的生成的pdf内容白屏或者黑屏 ,那你可以分段去生成base64编码 在这一步做同步并且吧截图模块做拆分,吧拿到的base64编码进行合并成一个base64编码
最终在去执行写入pdf文件的步骤(下面是base64编码合并),不需要的可以跳过这个代码。
// 友情提示 你要保存每个生成base64的编码以外 还要保存他们的元素高度进行求和不然图片的还原大小会不一致
let img1 = 'data:image/png;base64,iVBORwxxx'// 请替换base64内容 这个太长了我只是占位
let img2 = 'data:image/png;base64,iVBORwxxx'
* 合并多张图片,返回新的图片
* @param {Array} list 图片url数组
* @param {Number} cwith 画布宽度 默认500
* @param {Number} cheight 画布高度 默认500
function mergeImgs(list, cwith = 500, cheight = 500) {
return new Promise((resolve, reject) => {
const baseList = []
const canvas = document.createElement('canvas')
canvas.width = cwith
canvas.height = cheight * list.length
const context = canvas.getContext('2d')
list.map((item, index) => {
const img = new Image()
img.src = item
// 跨域
img.crossOrigin = 'Anonymous'
img.onload = () => {
context.drawImage(img, 0, cheight * index, cwith, cheight)
const base64 = canvas.toDataURL('image/png')
baseList.push(base64)
if (baseList[list.length - 1]) {
console.log(baseList)
// 返回新的图片
resolve(baseList[list.length - 1])
const urlList = [img1, img2]
mergeImgs(urlList).then(base64 => {
const imgDom = document.createElement('img')
imgDom.src = base64
document.body.appendChild(imgDom)
最终给大家看看生成的pdf文件打开的结果。