-
-
- [1. 使用jsPDF+html2canvas将页面转成图片然后导出](#1. 使用jsPDF+html2canvas将页面转成图片然后导出)
- [2. 自定义创建iframe调用iframe.print()进行页面打印转pdf导出](#2. 自定义创建iframe调用iframe.print()进行页面打印转pdf导出)
- [3. 使用react-to-print插件打印pdf](#3. 使用react-to-print插件打印pdf)
- [4. 利用@media print样式打印页面局部元素](#4. 利用@media print样式打印页面局部元素)
-
1. 使用jsPDF+html2canvas将页面转成图片然后导出
缺点:页面过长可能会导出失败,并且由于电脑分辨率的问题导致导出文件模糊不清
实现代码:
typescript
import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';
export function downToPdf(className: string, name: string) {
let elements = document.getElementsByClassName(className); // 获取指定类名的元素
if (elements.length === 0) {
return;
}
let element: any = elements[0]; // 使用第一个匹配到的元素
let w = element.offsetWidth; // 获取容器的宽度
let h = element.offsetHeight; // 获取容器的高度
let canvas = document.createElement("canvas");
canvas.width = w;
canvas.height = h;
// 默认横向没有滚动条的情况,因为offsetLeft有无滚动条的时候存在差值,因此
// translate的时候,要把这个差值去掉
html2canvas(element, { // 设置option可去除灰色色块
allowTaint: false,
useCORS: true,
scale: 1.2, // 用于渲染的比例。默认为浏览器设备像素比率
backgroundColor: '#F5F5F5',
windowWidth: element.scrollWidth,
windowHeight: element.scrollHeight
}).then(function (canvas) {
let contentWidth = canvas.width;
let contentHeight = canvas.height;
// 一页pdf显示html页面生成的canvas高度
let pageHeight = (contentWidth / 592.28) * 841.89;
// 未生成pdf的html页面高度
let leftHeight = contentHeight;
// 页面偏移
let position = 0;
// a4纸的尺寸[595.28,841.89]pt,html页面生成的canvas在pdf中图片的宽高
let imgWidth = 595.28;
let imgHeight = (592.28 / contentWidth) * contentHeight - 56.7; // 上下边距10mm
let pageData = canvas.toDataURL("image/jpeg", 1.0);
let pdf = new jsPDF("p", "pt", "a4");
// 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
// 当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < pageHeight) {
pdf.addImage(pageData, "JPEG", 0, 28.35, imgWidth, imgHeight);
} else {
// 分页
while (leftHeight > 0) {
pdf.addImage(pageData, "JPEG", 0, position + 28.35, imgWidth, imgHeight);
leftHeight -= pageHeight; // 加上页面高度和上下的页面距
position -= 841.89;
// 避免添加空白页
if (leftHeight > 0) {
pdf.addPage();
}
}
}
pdf.save(name + ".pdf");
});
}
2. 自定义创建iframe调用iframe.print()进行页面打印转pdf导出
缺点:对于echarts图表可能出现样式问题
代码实现:
typescript
export const printPDF = (dom: any) => {
// 将canvas元素转换为图片
convertCanvasToImages(dom);
};
// 写入iframe
function writeIframe(dom: any) {
const iframe: any = document.createElement("iframe");
iframe.style.position = "absolute";
iframe.style.width = "0";
iframe.style.height = "0";
iframe.style.top = "-10px";
iframe.style.left = "-10px";
document.body.appendChild(iframe);
const doc: any = iframe.contentDocument;
doc.open();
doc.write(getStyle() + getHtml(dom));
doc.close();
iframe.onload = function () {
iframe.contentWindow.print();
setTimeout(() => {
document.body.removeChild(iframe);
}, 100);
};
}
// 获取样式
function getStyle() {
const styles = document.querySelectorAll("style,link");
let str = "";
for (let i = 0; i < styles.length; i++) {
str += styles[i].outerHTML;
}
str += `<style>
@media print {
html,body{
height: auto;
margin: 0;
}
body{
zoom: 100%;
}
img{
max-width: 100% !important;
height: auto !important;
page-break-inside: auto;
break-inside: auto;
}
@page {
margin: 0;
}
}
</style>`;
return str;
}
// 获取dom
function getHtml(dom: any) {
return dom.outerHTML;
}
// 将canvas元素转换为图片
function convertCanvasToImages(dom: any) {
const canvasElements = dom.querySelectorAll('canvas');
let convertedCount = 0;
if (canvasElements.length === 0) {
writeIframe(dom);
return;
}
canvasElements.forEach((canvas: any) => {
const img = document.createElement('img');
img.src = canvas.toDataURL('image/png');
img.style.width = '100%';
img.style.height = 'auto';
canvas.parentNode.replaceChild(img, canvas);
convertedCount++;
if (convertedCount === canvasElements.length) {
writeIframe(dom);
}
});
}
3. 使用react-to-print插件打印pdf
缺点:对于大量echarts图表可能会随机几个出现样式问题
代码实现:
- 下载: yarn add react-to-print
- 引入插件
import {useReactToPrint} from "react-to-print";
- 使用
typescript
import React, { useRef } from 'react';
import { useReactToPrint } from 'react-to-print';
const PrintComponent = React.forwardRef((props, ref) => {
return (
<div ref={ref} style={{ width: '100%', height: 'auto' }}>
{/* 你的长内容 */}
<div>Page 1</div>
<div>Page 2</div>
<div>Page 3</div>
{/* 更多内容 */}
</div>
);
});
const App = () => {
const printRef = useRef();
// 打印功能
const handlePrint = useReactToPrint({
content: () => printRef.current,
pageStyle: `
@page {
size: A4;
margin: 0;
}
@media print {
body, html {
height: auto;
overflow: initial !important; // 重要,如果不分页需要加上
margin: 0;
}
body{
zoom: 100%;
}
img{
max-width: 100% !important;
height: auto !important;
page-break-inside: auto;
break-inside: auto;
}
}`,
removeAfterPrint: true,
documentTitle: `报告名称`,
});
return (
<div>
<PrintComponent ref={printRef} />
<button onClick={handlePrint}>打印</button>
</div>
);
};
export default App;
4. 利用@media print样式打印页面局部元素
使用该方式,不需要更改浏览器的原生打印,只需要样式控制即可打印出指定部分的内容
- 页面结构
- 样式控制
css
@media print {
@page {
size: A4;
margin: 0;
}
.ant-button, #zdns-header, .ant-notification {
display: none;
}
.reportTemplateScene {
border: none;
margin: 0;
height: auto;
overflow: initial !important;
page-break-inside: avoid; /* 避免分页中断 */
}
#root > div {
height: auto; /* 确保内容不会被固定高度限制 */
}
/* 移除祖父节点的滚动条样式 */
.ant-spin-container::-webkit-scrollbar,
.ant-spin-container::-webkit-scrollbar-thumb,
.ant-spin-container::-webkit-scrollbar-track {
display: none;
}
/* 确保祖父节点在打印时不显示滚动条 */
.ant-spin-container {
overflow: hidden !important;
padding: 0 !important;
}
/* 确保所有内容都可见 */
body, html {
overflow: visible !important;
}
/* 确保所有内容都打印出来 */
* {
visibility: visible !important;
}
}