【Vue 项目】html2canvas + jspdf 实现将页面内容生成 PDF 及相关问题解决方案(页面被分页分割问题、html2canvas生成图片只有一半)

由于在自己的工作和学习过程中,只查看某个大佬的教程或文章无法满足自己的学习需求和解决遇到的问题,所以自己在追赶大佬们步伐的基础上,又自己总结、整理、汇总了一些资料,方便自己理解和后续回顾,同时也希望给大家带来帮助,所以才写下该篇文章。在本文中,所有参考或引用大佬们文章内容的位置,都附上了原文章链接,您可以直接前往查阅观看。在原文章内容的基础上,若无任何补充内容,同时避免直接大段摘抄大佬们的文章,该情况下也只附上了原文章链接供大家学习。本文旨在总结归纳,并希望给大家提供帮助,未用作任何商用用途。文章内容如有错误之处,望各位大佬指出。如果涉及侵权行为,将会第一时间对文章进行删除。


👉 个人博客主页 👈
📝 一个努力学习的程序猿


其他前端组件使用和踩坑记录文章,欢迎您查看:



介绍

1、html2canvas

该插件允许我们直接在浏览器上对网页或其部分进行“截图”操作,但是屏幕截图基于 DOM,这一点很关键。这就会导致截图的结果,很可能不是自己想要的。而且也会受到 CSS 的影响。在后续的使用中就可以发现这一问题,而且很有可能导致 bug 的发生(本文中就记录了一个)。

而它的优势就是不需要来自服务器的任何渲染,因为整个图像是在浏览器上生成的。但是,也正因为它严重依赖浏览器,所以这个库不适合在 nodejs 中使用。它也会受到浏览器策略的限制,因此呈现跨域内容也需要代理来解决
在这里插入图片描述


2、jspdf

该插件就是用来生成 PDF 的。
在这里插入图片描述


在使用时,利用 html2canvas 将页面转换成图片,然后再通过 jspdf 将图片的 base64 生成 pdf 文件。

而为什么要将生成 pdf 分成两步,使用两个插件?

【关于更详细的使用内容,为了避免大篇幅引用,您可以直接前往原链接中查阅,本文后续主要针对遇到的问题】

首先只用 jspdf 生成 pdf 的话,用法有些繁琐。感兴趣的话,可以查看官方demo的使用方式:
npmjs.com/package/jspdfjsPDF Demos

其次 jspdf 不支持中文。如果您真想直接使用 jspdf,可以查看该文章:jspdf简单使用

除此以外,最关键的就是很难保持原有页面的样式。这样生成的 pdf 可能既不美观,也不满足要求,而且工作量还很大。

因此,最快捷简便的办法就是先使用 html2canvas 将页面转换成图片(html2canvas 使用方法),然后再将图片导成 pdf。用法很简单。使用方法在下面附上原文链接。


使用方法

在 Vue 项目中的使用方法,为了避免大篇幅引用,您可以直接前往以下文章查阅:前端实现HTML转PDF下载的方式 。经测试完全可用。在这里将不做复制粘贴。但是这个方式下,会导致一些其他的问题。接下来将对这些问题进行总结。


问题一、分页分割问题

如果使用上述方案,那么当页面长度超过指定的高度(默认是 a4),这时页面的内容很可能会出现被分页分开,而导致页面内容分割的情况。如下图所示。

在这里插入图片描述
【图片截取自:jsPDF 分页分割问题踩坑

在网上找了很久的答案,似乎大家都会被这样的问题所困扰。解决方案大致也就这几种,总结如下:

1、页面设计的时候就考虑到分页的情况,通过 CSS 设置高度来避免页面被分割。当然这样工作量就很大了。

2、只使用 jspdf。如果各位想这么做,当然也可以,工作量也会很大,在前文也有相关表述,附上了原文链接。

3、如果没有特殊需求,最优解就是不分页。这样就能保留页面内容,而且也不会出现问题。但是一个最致命的缺陷也就在这里,pdf 要是不分页的话,打印机打出来得多长一张纸?

所以,从目前来看,如果没有打印等特殊需求,可以使用不分页的情况,不费时不费力。如果需要分页打印,那只能考虑方法1了。如果您有更好的方法,欢迎在评论区留言。

关于 方法1 + 方法3 更详细的说明,为了避免大篇幅引用,您可以直接前往以下文章查阅:jsPDF 分页分割问题踩坑

在这里为了方便查阅,简单粘贴一下 方法3 实现代码:

htmlToPdf.js:

// 导出页面为PDF格式
import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'
export default {
  install(Vue, options) {
    Vue.prototype.getPdf = function() {
      const title = this.htmlTitle;
      const scale = 2;
      html2Canvas(document.querySelector('#pdfDom'), {
        allowTaint: true, // 开启跨域
        scale // 提升画面质量,但是会增加文件大小
      }).then(function(canvas) {
        const contentWidth = canvas.width / scale;
        const contentHeight = canvas.height / scale;
        const PDF = new JsPDF('', 'pt', [contentWidth, contentHeight]);
        const pageData = canvas.toDataURL('image/jpeg', 1.0);
        PDF.addImage(pageData, 'JPEG', 0, 0, contentWidth, contentHeight);
        PDF.save(title + '.pdf');
      })
    }
  }
}

main.js:

import htmlToPdf from '@/utils/htmlToPdf';
Vue.use(htmlToPdf);

html.vue:

<div id="pdfDom">
   <!-- 要下载的HTML页面 -->
  <div>内容</div>
</div>
<el-button type="primary" size="small" @click="getPdf('#pdfDom')">点击下载</el-button>
export default {
  data () {
    return {
      htmlTitle: '页面导出PDF文件名'
    }
  }
}

问题二、html2canvas生成图片只有一半

首先先来看一下出现问题的效果:
在这里插入图片描述
可以发现生成的 pdf 文件中,目前只生成了右半部分的内容。对于问题的锁定也是很快的,问题只能出现在 html2canvas,也就是截图操作上。在前文有提到,html2canvas 基于 DOM,且会受到 CSS 的影响,那么问题其实也是出在这个方向上。

在网上查询相关解决方案时,发现所有方法都无法解决自己的问题。为了避免大篇幅引用,各位如有需要,可以前往这些文章查看,看看是否能解决问题

html2canvas 生成图片 生成一半有图一半空白 是什么原因导致

html2canvas生成的图片偏移不完整的解决方法

html2canvas,则只绘制图像的一半或不绘制某些图像

html2canvas (踩坑) 网络图片显示不出来&生成图片只有一半或者空白&文字显示不出来问题处理

而经过网上冲浪一波,最后总结下来的结论就是以下几点:

1、首先查看需要生成图片的 DOM 结构,是否渲染完成。

2、如果渲染完成,再查看是否由于页面内容超出了 宽 / 高 而被隐藏导致。(比如受到了 CSS overflow 的影响,即出现滚滑轮)

3、比较玄学的就是在触发生成图片后,不要滚动页面。(没这个操作请忽略)

4、再玄学点就是调整 html2canvas 版本,听闻可以把 html2canvas 版本切换到 1.0.0-rc.4 尝试。

不过以上方法都没有解决自己的问题。那么问题到底出现在哪里?

其实在寻找解决方法的过程中,已经可以发现一些端倪。打印出图片的效果,很有可能是受到了 CSS 的影响。果然,网上也有提到:ios部分元素丢失、部分css3属性不支持、图片使用 img 标签不使用 background-img 、图片跨域问题等等。


所以从 CSS 入手果然发现了问题。因为我只打印出了右半部分内容,所以该问题大概率出现在我外侧包裹的元素上。比如,我是这样写的:

<el-card id="pdfDom" class="cardPdf">
	<!-- 内容 -->
</el-card>

对于 el-card 的样式就放在了 cardPdf 上。其中,由于之前提到分页分割问题,所以这里我其实是指定宽度高度去生成这样一张图片。那么在不同分辨率的页面下,就肯定无法做到居中处理。所以为了居中,我加了这样一段 CSS:

>>> .cardPdf {
  width: 1291px;
  padding: 20px 30px;
  position: relative;
  left: 50%;
  transform: translateX(-50%);
}

而问题就碰巧出现在了居中的这段 CSS 上。

由于在最开始没把问题归结到 CSS 上,所以花费了很长时间。最终在同事的提醒下,发现了这个问题。从源码中可以发现:html2canvas 它支持的 css transform 只有 2d 和 3d 的矩阵变换。

那为了解决这个问题去居中,就可以使用这样的方式:

>>> .cardPdf {
  width: 1291px;
  padding: 20px 30px;
  margin: 0 auto;
}

最终发现问题解决:

在这里插入图片描述

所以,如果各位也有部分内容展示不出来的情况,就可以从这些元素入手。看是不是因为某个 CSS 属性不支持 又或者 是因为某个标签不支持。由于不支持的要素可能比较多,也很难总结,出现问题也就只能一点一点排查了。


问题三、pdfjs 生成图片大小异常

最近在使用过程中又遇到了展示不完整的情况,可以很明显的看到右侧文字内容未展示完全:

在这里插入图片描述
根据之前的道理,首先把问题锁定在 html2canvas 这一步上,怀疑生成的图片就是残缺的,即 CSS 问题。所以我去掉了所有的 CSS,变成纯文本后,输出查看 base64 时发现,此时图片是完整的。

在这里插入图片描述

那么也就说明,问题不是 html2canvas,这回是 jspdf 出现了问题。

代码使用的是上面页面分割解决方案的代码(这次没注册成全局变量,而选择了局部引用)。

// 导出页面为PDF格式
import html2Canvas from 'html2canvas';
import JsPDF from 'jspdf';
export const getPdf = (id, title) => {
    const scale = 2;
    html2Canvas(document.querySelector(id), {
        allowTaint: true, // 开启跨域
        scale // 提升画面质量,但是会增加文件大小
    }).then(function(canvas) {
        const contentWidth = canvas.width / scale;
        const contentHeight = canvas.height / scale;
        const PDF = new JsPDF('', 'pt', [contentWidth, contentHeight]);
        console.log('content', contentWidth, contentHeight);
        const pageData = canvas.toDataURL('image/jpeg', 1.0);
        PDF.addImage(pageData, 'JPEG', 0, 0, contentWidth, contentHeight);
        PDF.save(title + '.pdf');
    })
}
const test = document.getElementById('pdfDom');
console.log('元素大小', test.clientWidth, test.clientHeight);

通过输出 contentWidth、contentHeight、元素的宽高,结果发现相距不大。
在这里插入图片描述
因此,既然宽高没错,那问题只能出在 addImage 的坐标设定和绘制上了。

果不其然,通过调整其中的参数就可以解决问题(参数如何确定暂时未知,目前是手动测试发现 1.5 合适。后来发现,在不同的页面,该值的大小将不是个确切数字)。

PDF.addImage(pageData, 'JPEG', 0, 0, contentWidth / 1.5, contentHeight / 1.5);

如果各位有类似的问题,也可根据上述思路进行测试。但目前还不清楚为什么需要再有个除数以及该除数的比例该如何计算,而为什么在上文解决页面分割时没遇到此问题。如果各位能够答疑解惑,也欢迎在评论区提问。


其他参考文章

在查询解决方法的过程中,也发现一些有用的文章,如果后续遇到类似问题,也可以前往查看:

使用html2canvas在前端生成图片及相关bug问题整理


由于在自己的工作和学习过程中,只查看某个大佬的教程或文章无法满足自己的学习需求和解决遇到的问题,所以自己在追赶大佬们步伐的基础上,又自己总结、整理、汇总了一些资料,方便自己理解和后续回顾,同时也希望给大家带来帮助,所以才写下该篇文章。在本文中,所有参考或引用大佬们文章内容的位置,都附上了原文章链接,您可以直接前往查阅观看。在原文章内容的基础上,若无任何补充内容,同时避免直接大段摘抄大佬们的文章,该情况下也只附上了原文章链接供大家学习。本文旨在总结归纳,并希望给大家提供帮助,未用作任何商用用途。文章内容如有错误之处,望各位大佬指出。如果涉及侵权行为,将会第一时间对文章进行删除。


👉 个人博客主页 👈
📝 一个努力学习的程序猿


其他前端组件使用和踩坑记录文章,欢迎您查看:

  • 30
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: HTML2Canvas是一个将HTML DOM元素转换为canvas的JavaScript库,而jspdf是一个用于生成PDF文档的JavaScript库。因此,您可以使用HTML2Canvas将HTML DOM元素转换为Canvas,然后使用jspdf将Canvas转换为PDF。 要处理分页图片和被截断的文本问题,您可以使用以下步骤: 1. 将HTML DOM元素转换为Canvas。您可以使用HTML2Canvas库来完成此操作。请确保在转换时包含所有的分页图片和文本。 2. 将所有的Canvas合并在一起,以便生成单个PDF文件。您可以使用canvas.toDataURL()方法将每个Canvas转换为图像数据URL。然后,将所有的图像数据URL合并在一起,并使用jspdf将它们转换为PDF。 3. 处理被截断的文本。如果文本被截断,您可以使用CSS的white-space属性来处理它。例如,您可以设置white-space:pre-wrap属性来打破单词并允许文本换行。 4. 处理分页图片。如果您有分页图片,您可以将它们分成多个Canvas,并将它们一起合并到PDF中。这将确保每个分页图片都在正确的位置显示。 总的来说,使用HTML2Canvasjspdf生成PDF文档是一个十分强大的工具。通过遵循上述步骤,您可以轻松地处理文本被截断和分页图片问题,并生成高质量的PDF文件。 ### 回答2: html2canvas是一个javascript库,用于将HTML元素渲染为Canvas,并提供了将Canvas导出为图片的功能。而jspdf是另一个javascript库,用于生成PDF文件。 使用html2canvasjspdf可以实现前端生成PDF文件的功能。首先,使用html2canvas将HTML页面内容渲染为Canvas。然后,将Canvas转换为图像数据,并将图像数据添加到jspdfPDF文件中。最后,可以将生成PDF文件保存或下载。 但是在处理分页时,把整个HTML页面转换为Canvas,可能会导致图片和文字被截断的问题。这是因为Canvas在渲染时有大小限制。 为了解决这个问题,可以通过在特定位置手动分页来控制页面内容的展示。首先,确定每一页需要展示的内容,并在对应的位置手动分页。具体的做法是使用CSS的page-break属性来设置页面分页位置,以确保每一页不会出现图片和文字被截断的情况。 在使用html2canvas时,可以通过设置ignoreElements属性来忽略某些元素不进行渲染,以避免被截断。例如,对于需要分页图片,可以在转换为Canvas时,将其设置为忽略的元素,保证图片完整显示在各个页面中。 总而言之,使用html2canvasjspdf可以在前端实现PDF文件的生成,但处理分页时需要注意图片和文字被截断的问题。通过手动设置分页位置和忽略特定元素,可以解决这个问题,确保生成PDF文件中的内容完整可读。 ### 回答3: html2canvas是一个JavaScript库,可以将HTML元素转换为canvas画布,并且可以配合jspdf生成PDF文件。 要处理分页图片和文字被截断的问题,可以按照以下步骤进行: 1. 引入html2canvasjspdf库。 2. 创建一个用于生成PDF的按钮或者其他触发事件。 3. 使用html2canvas库将需要转换为PDF的HTML元素转换为canvas画布。可以使用html2canvas()方法,并将需要转换的元素传递给该方法。 4. 使用toDataURL()方法将canvas画布转换为一个base64编码的图像字符串。 5. 创建一个jspdf实例。可以使用`new jsPDF()`创建一个新的jspdf实例。 6. 使用jspdf的addImage()方法将之前生成的base64编码的图像字符串添加到页面上。可以通过指定x、y坐标和图像的宽度和高度来设置图像在PDF中的位置和尺寸。 7. 使用jspdf的save()方法保存生成PDF文件。 对于分页图片和文字被截断的问题html2canvasjspdf并不能自动处理。可以通过以下方法来解决: 1. 对于图片,可以在将HTML元素转换为canvas之前,通过调整元素的尺寸、位置或者裁剪来确保图片完整显示在一个页面中。 2. 对于文字,可以通过调整元素的字体大小、行高或者缩减内容来确保文字完整显示在一个页面中。 3. 如果需要处理多页的情况,可以使用循环或者递归的方式将分页内容分别转换为canvas,并通过jspdf的addPage()方法在PDF中添加新的页面。 总结来说,通过html2canvasjspdf配合使用,可以在前端生成PDF文件。对于处理分页图片和文字被截断的问题,需要手动调整HTML元素的尺寸、位置和内容,以确保完整显示在PDF中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只爭朝夕不負韶華

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值