html2canvas+jsPDF导出超长网页的PDF

项目需求:有一个网页大概60000px的高度,现在需要导出为PDF


index.vue

<template>
  <div class="ctn">
    <div class="pdf-ctn">
      <div class="pdf-panel" >
        <div class="pdf-inside-panel" id="myList">
          <div v-for="(item, index) in 3000" :key="index" style="height: 20px">
            {{index}}---我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我是测试我的高度{{
              (index+1)*20
            }}
          </div>

        </div>
      </div>
      <div
        class="pdf-header"
        style="
          font-weight: bold;
          padding: 15px 8px;
          width: 100%;
          border-bottom: 1px solid rgba(0, 0, 0, 0.85);
          color: rgba(0, 0, 0, 0.85);
          position: fixed;
          top: -100vh;
        "
      >
        页头
      </div>
      <div
        class="pdf-footer"
        style="
          font-weight: bold;
          padding: 15px 8px;
          width: 100%;
          border-top: 1px solid rgba(0, 0, 0, 0.85);
          position: fixed;
          top: -100vh;
        "
      >
        <div
          style="
            display: flex;
            justify-content: center;
            align-items: center;
            padding-top: 5px;
          "
        >
          我是页尾
        </div>
        <div
          style="
            display: flex;
            justify-content: center;
            align-items: center;
            margin-top: 20px;
          "
        >
          第
          <div class="pdf-footer-page"></div>
          页 / 第
          <div class="pdf-footer-page-count"></div>
          页
        </div>
      </div>
    </div>
    <div>
      <a-button
        style="top: 50px; left: 1450px; position: fixed"
        @click="handleOutput"
      >
        测试导出
      </a-button>
    </div>
  </div>
</template>

<script>
import { message } from "ant-design-vue";
import { outCanvas } from "../scroll";
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  methods: {
    async handleOutput() {
      const element = document.querySelector("#myList");
      const header = document.querySelector(".pdf-header");
      const footer = document.querySelector(".pdf-footer");
        await outCanvas(element);
        let endTime = new Date().getTime();
        let timeElapsed = endTime - startTime; // 获取时间差(毫秒)
        console.log(`函数运行时间: ${timeElapsed} 毫秒`);
      } catch (error) {
        console.log(error)
        message.error(
          typeof error === "string" ? error : JSON.stringify(error)
        );
      }
    },
  }
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
.ctn {
  .pdf-ctn {
    width: 1300px;
    .pdf-panel {
      position: relative;
    }
  }
}
</style>

JS

import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
import { message } from 'ant-design-vue';


// jsPDFs实例
let pdf = new jsPDF({
    unit: 'pt',
    format: 'a4',
    orientation: 'p',
    // format: [550, 550]
});

// 对图片进行等比缩放
function resizeImage(imgWidth, imgHeight, maxWidth = 590) {
    // 计算当前图片的宽高比
    const ratio = imgWidth / imgHeight;

    // 如果最大宽度小于当前宽度,则按最大宽度进行缩放
    if (imgWidth > maxWidth) {
        return {
            newWidth: maxWidth,
            newHeight: maxWidth / ratio
        };
    } else { // 否则,图片本身就在允许的最大宽度内,不需要缩放
        return {
            newWidth: imgWidth,
            newHeight: imgHeight
        };
    }
}


async function toCanvas(element,scrolledHeight=0,viewHeight=window.innerHeight) {
    // 放大倍率
    const scaleRatio = window.devicePixelRatio * 2
    const canvas = await html2canvas(element, {
        scale: scaleRatio,
        useCORS: true,
        width: document.querySelector("#myList").scrollWidth,
        height: Math.min(element.scrollHeight,  viewHeight),
        windowWidth: document.querySelector("#myList").scrollWidth,
        windowHeight: document.querySelector("#myList").scrollHeight,
        x: 0,
        y: scrolledHeight,
    })
    let canvasImg = canvas.toDataURL("image/jpeg",1);
    return { width:canvas.width, height:canvas.height, data: canvasImg}
}


// 循环生成PDF
let pdfImgTop = 0
let pageHeight = 0
async function loopGeneratePDF(targetElement, scrolledHeight = 0, viewHeight =window.innerHeight) {
    const A4_HEIGHT = 900
    if (scrolledHeight >= targetElement.scrollHeight) {
        message.success("生成PDF成功");
        return;
    }
    const { data: imgData, height, width } = await toCanvas(targetElement, scrolledHeight, viewHeight);
    // console.log("图片",imgData)
    const { newWidth, newHeight } = resizeImage(width, height);
    pdf.addImage(imgData, 'JPEG', 0, pdfImgTop, newWidth, newHeight);
    const pages = pdf.internal.getNumberOfPages()
    message.success(`生成第${pages}页`)
    // 下一次需要截取的开始高度
    scrolledHeight += Math.floor(height / 2);
    pdfImgTop += newHeight;
    // 如果当前页内容不足一页A4纸的高度,则递归调用并调整视图高度
    if (A4_HEIGHT>scrolledHeight) {
        // 剩余页面的高度
        pageHeight = A4_HEIGHT - scrolledHeight;
        return loopGeneratePDF(targetElement, scrolledHeight, pageHeight);
    }
    else {
        if(targetElement.scrollHeight - scrolledHeight > A4_HEIGHT || pdfImgTop>A4_HEIGHT){
            pdf.addPage();
            pdfImgTop = 10;
        }
        return loopGeneratePDF(targetElement, scrolledHeight-20);
    }

}

export const outCanvas = async function (targetElement) {
    if (!(targetElement instanceof HTMLElement)) {
        return;
    }
    await loopGeneratePDF(targetElement,0,window.innerHeight)
   return pdf.save('test.pdf');
}



### 解决 HTML2CanvasjsPDF 组合使用时导出内容不完全显示的问题 当遇到HTML2CanvasjsPDF组合使用时导出内容无法完全显示的情况,可以采取以下几种解决方案来确保所有内容都能被正确捕获并转换成PDF。 #### 方法一:调整页面高度适应整个文档 通过计算网页的实际总高度,并将其设置为单个大画布的高度,从而避免分页带来的截断问题。这种方法能够有效处理长页面的截图需求[^1]。 ```javascript const totalHeight = document.body.scrollHeight; html2canvas(document.body, { height: totalHeight, }).then(canvas => { const imgData = canvas.toDataURL('image/png'); var pdf = new jsPDF('p', 'mm', 'a4'); var width = pdf.internal.pageSize.getWidth(); var height = (canvas.height * width) / canvas.width; // 添加图像至PDF pdf.addImage(imgData, 'PNG', 0, 0, width, height); // 如果超过一页,则继续添加新页直到完成全部内容 let position = 0; while(position < height){ if((position + height) >= pdf.internal.pageSize.getHeight()){ pdf.addPage(); position -= pdf.internal.pageSize.getHeight(); } pdf.addImage(imgData, 'PNG', 0, position, width, height); position += height; } pdf.save("document.pdf"); }); ``` #### 方法二:逐部分渲染再拼接 对于含有大量动态加载或异步资源(如图片、视频等)的复杂页面,建议采用分割屏幕的方法——即先获取每屏的数据URL,之后把这些片段合成完整的PDF文件。此法能较好地应对跨多个视口范围内的元素捕捉难题[^2]。 ```javascript async function captureScreen() { const sections = []; let yPosition = window.innerHeight; // 初始位置等于窗口可见区间的高 do { await html2canvas(document.scrollingElement).then(canvas => { sections.push({ dataUrl: canvas.toDataURL(), height: Math.min(yPosition, document.documentElement.offsetHeight - yPosition), }); }); // 向下滚动一个窗口大小的距离 window.scrollBy(0, window.innerHeight); // 更新y轴坐标 yPosition += window.innerHeight; } while (yPosition < document.documentElement.offsetHeight); generatePdfFromSections(sections); } function generatePdfFromSections(sections) { const doc = new jsPDF(); sections.forEach(section => { const {dataUrl, height} = section; const pageWidth = doc.internal.pageSize.getWidth(); const scaleRatio = pageWidth / screen.availWidth; const scaledHeight = height * scaleRatio; doc.addImage(dataUrl, "JPEG", 0, 0, pageWidth, scaledHeight); if(scaledHeight > doc.internal.pageSize.getHeight()) { doc.setPage(doc.getNumberOfPages()); doc.addPage(); } }); doc.save(`exported_document.pdf`); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值