html转pdf 分页问题 包含自定义页头页尾
先自行下载依赖 两个
npm install html2canvas jspdf
然后创建个 js 文件
import html2canvas from 'html2canvas';
import jsPDF from "jspdf";/** a4纸的尺寸[595.28,841.89], 单位毫米 */
const [PAGE_WIDTH, PAGE_HEIGHT] = [595.28, 841.89];const PAPER_CONFIG = {/** 竖向 */portrait: {height: PAGE_HEIGHT,width: PAGE_WIDTH,contentWidth: 595.28,},/** 横向 */landscape: {height: PAGE_WIDTH,width: PAGE_HEIGHT,contentWidth: 841.89,},
};// 将元素转化为canvas元素
// 通过 放大 提高清晰度
// width为内容宽度
async function toCanvas(element, width) {if (!element) return { width, height: 0 };const refDom = element.getBoundingClientRect();// canvas元素const canvas = await html2canvas(element, {// allowTaint: true, // 允许渲染跨域图片scale: window.devicePixelRatio * 1, // 增加清晰度useCORS: false, // 允许跨域// taintTest: false,width: refDom.width,height: refDom.height,});// 获取canvas转化后的宽高const { width: canvasWidth, height: canvasHeight } = canvas;// html页面生成的canvas在pdf中的高度const height = (width / canvasWidth) * canvasHeight;// 转化成图片Dataconst canvasData = canvas.toDataURL("image/jpeg", 1.0);return { width, height, data: canvasData };
}/*** 生成pdf(A4多页pdf截断问题, 包括页眉、页脚 和 上下左右留空的护理)* @param param0* @returns*/
export async function outputPDF({/** pdf内容的dom元素 */element,/** 页脚dom元素 */footer,/** 页眉dom元素 */header,/** pdf文件名 */filename,/** a4值的方向: portrait or landscape */orientation = "portrait",
}) {if (!(element instanceof HTMLElement)) {return;}if (!["portrait", "landscape"].includes(orientation)) {return Promise.reject(new Error(`Invalid Parameters: the parameter {orientation} is assigned wrong value, you can only assign it with {portrait} or {landscape}`));}const [A4_WIDTH, A4_HEIGHT] = [PAPER_CONFIG[orientation].width,PAPER_CONFIG[orientation].height,];/** 一页pdf的内容宽度, 左右预设留白 */const { contentWidth } = PAPER_CONFIG[orientation];// eslint-disable-next-line new-capconst pdf = new jsPDF({unit: "pt",format: "a4",orientation,});// 一页的高度, 转换宽度为一页元素的宽度const { width, height, data } = await toCanvas(element, contentWidth);// 添加function addImage(_x, _y, pdfInstance, base_data, _width, _height) {pdfInstance.addImage(base_data, "JPEG", _x, _y, _width, _height);}// 增加空白遮挡function addBlank(x, y, _width, _height) {pdf.setFillColor(255, 255, 255);pdf.rect(x, y, Math.ceil(_width), Math.ceil(_height), "F");}// 页脚元素 经过转换后在PDF页面的高度const { height: tFooterHeight, data: footerData } = footer? await toCanvas(footer, contentWidth): { height: 0, data: undefined };// 页眉元素 经过转换后在PDF的高度const { height: tHeaderHeight, data: headerData } = header? await toCanvas(header, contentWidth): { height: 0, data: undefined };// 添加页眉async function addHeader(headerElement) {headerData &&pdf.addImage(headerData,"JPEG",0,0,contentWidth,tHeaderHeight);}// 添加页脚async function addFooter(pageNum, now, footerElement) {if (footerData) {pdf.addImage(footerData,"JPEG",0,A4_HEIGHT - tFooterHeight,contentWidth,tFooterHeight);}}// 距离PDF左边的距离,/ 2 表示居中const baseX = (A4_WIDTH - contentWidth) / 2; // 预留空间给左边// 距离PDF 页眉和页脚的间距, 留白留空const baseY = 0;// 除去页头、页眉、还有内容与两者之间的间距后 每页内容的实际高度const originalPageHeight =A4_HEIGHT - tFooterHeight - tHeaderHeight - 2 * baseY;// 元素在网页页面的宽度const elementWidth = element.offsetWidth;// PDF内容宽度 和 在HTML中宽度 的比, 用于将 元素在网页的高度 转化为 PDF内容内的高度, 将 元素距离网页顶部的高度 转化为 距离Canvas顶部的高度const rate = contentWidth / elementWidth;// 每一页的分页坐标, PDF高度, 初始值为根元素距离顶部的距离const pages = [rate * getElementTop(element)];// 获取该元素到页面顶部的高度(注意滑动scroll会影响高度)function getElementTop(contentElement) {if (contentElement.getBoundingClientRect) {const rect = contentElement.getBoundingClientRect() || {};const topDistance = rect.top;return topDistance;}}// 遍历正常的元素节点function traversingNodes(nodes) {for (const element of nodes) {const one = element;/** *//** 注意: 可以根据业务需求,判断其他场景的分页,本代码只判断表格的分页场景 *//** */// table的每一行元素也是深度终点const isTableRow =one.classList && one.classList.contains("el-table-row");// console.log(isTableRow, "-isTableRow");// 对需要处理分页的元素,计算是否跨界,若跨界,则直接将顶部位置作为分页位置,进行分页,且子元素不需要再进行判断const { offsetHeight } = one;// 计算出最终高度const offsetTop = getElementTop(one);// dom转换后距离顶部的高度// 转换成canvas高度const top = rate * offsetTop;const rateOffsetHeight = rate * offsetHeight;if (top - (pages.length > 0 ? pages[pages.length - 1] : 0) >=originalPageHeight) {pages.push((pages.length > 0 ? pages[pages.length - 1] : 0) + originalPageHeight);}// 若 距离当前页顶部的高度 加上元素自身的高度 大于 一页内容的高度, 则证明元素跨页,将当前高度作为分页位置else if (top +rateOffsetHeight -(pages.length > 0 ? pages[pages.length - 1] : 0) >originalPageHeight &&top !== (pages.length > 0 ? pages[pages.length - 1] : 0)) {if (one.childNodes && one.childElementCount > 0) {traversingNodes(one.childNodes);} else {pages.push(top);}}}}// 深度遍历节点的方法traversingNodes(element.childNodes);// 对pages进行一个值的修正,因为pages生成是根据根元素来的,根元素并不是我们实际要打印的元素,而是element,// 所以要把它修正,让其值是以真实的打印元素顶部节点为准const newPages = pages.map((item) => item - pages[0]);// 根据分页位置 开始分页for (let i = 0; i < newPages.length; ++i) {// 根据分页位置新增图片addImage(baseX,baseY + tHeaderHeight - newPages[i],pdf,data,width,height);// 将 内容 与 页眉之间留空留白的部分进行遮白处理addBlank(0, tHeaderHeight, A4_WIDTH, baseY);// 将 内容 与 页脚之间留空留白的部分进行遮白处理addBlank(0, A4_HEIGHT - baseY - tFooterHeight, A4_WIDTH, baseY);// 对于除最后一页外,对 内容 的多余部分进行遮白处理if (i < newPages.length - 1) {// 获取当前页面需要的内容部分高度const imageHeight = newPages[i + 1] - newPages[i];// 对多余的内容部分进行遮白addBlank(0,baseY + imageHeight + tHeaderHeight,A4_WIDTH,A4_HEIGHT - imageHeight);}// 添加页眉if (header) {await addHeader(header);}// 添加页脚if (footer) {await addFooter(newPages.length, i + 1, footer);}// 若不是最后一页,则分页if (i !== newPages.length - 1) {// 增加分页pdf.addPage();}}pdf.save(filename);// 内嵌到微信小程序中得下载// var blob = pdf.output("datauristring");// uni.webView.postMessage({// data: {// type: "blob",// imageData: blob,// },// });// uni.webView.navigateBack();return true;
}
然后再相关页面去引入 则可以使用了。