如何将网页转成 PDF,超出一页时如何解决

2026-09-07 经验分享

订单、合同、报表预览经常要把当前页存一份给对方。这版用 jQuery 1.9.1:html2canvas 把指定节点画成一张长图,jsPDF 再按 A4 往 PDF 里贴;高度超过一页,就把这张图按页切开。

嵌入项目时,按后面「怎么嵌进业务页,只导出指定容器」那节,把截取节点换成业务里的内容区。

网页转 PDF 用到哪些文件

图片1.png

四个脚本顺序不能乱:jQuery、html2canvas、jsPDF,最后才是 index.js。jQuery 不在业务脚本前面,$ 还没定义;html2canvas 不在 jsPDF 前面,后面 html2canvas(...) 也调不起来。index.js 包在 $(function () {}) 里,放 <head> 也会等 DOM 好了再绑。

图片2.png

业务就这几个名字,后面会反复碰到:

1、#pdfContent:真正拿去截的那块节点

2、#renderPdf:导出按钮,放在内容区外面

3、A4_WIDTH / A4_HEIGHT:A4 宽高,单位是 pt

4、PDF_MARGIN:左右页边距

5、pageHeight、leftHeight、position:分页用的三个数

按钮不要放进 #pdfContent。放进去,PDF 里会带上「导出 PDF」这颗按钮。

截取节点和 A4 页尺寸怎么配

点按钮之后就两步:先截图,再按 A4 把图塞进 PDF。

图片3.png

html2canvas 吃的是一个 DOM 节点,不是整份 document。本例传 pdfContent,侧栏、导航、正文一起在这个容器里,导出的就是这一块。业务页只想导出订单明细、不要顶栏和按钮,就把 id 换成那块容器。

useCORS: true 是给外链图用的。图在自己站点或 images/ 里,不写也能画出来;图在 CDN 上、又没 CORS 头,canvas 上那块是空白。这坑我见过不止一次:PDF 里文字在、图没了,先查图片域名允不允许跨域读像素。

截完得到 canvas,宽高是像素。PDF 这边单位是 pt。A4 在 jsPDF 里是 595.28 × 841.89。左右各留 PDF_MARGIN(本例 20),图能用的宽度就是:

图片4.png

说白了:按「A4 可打印宽度 / canvas 宽度」把整张长图等比缩小。imgHeight 往往比 841.89 大,一页塞不下,这就要靠下一节的分页。

new jsPDF("", "pt", "a4") 三个参数:方向空字符串按纵向,单位 pt,纸张 a4。后面 addImage 的坐标、宽高都按 pt 算,别跟 canvas 像素混用。

分页高度和 position 偏移怎么算

我第一次看也卡了一下。canvas 是一张通长的图,PDF 是一叠 A4。不能把长图整张塞进第一页,要按页高把同一张图反复贴、每次往上挪。

先把「一页 A4 对应多少 canvas 像素」算出来:

图片5.png

pageHeight 走 canvas 像素,A4_HEIGHT 走 PDF 的 pt,两套坐标系。leftHeight 一开始等于整张图的像素高度,每贴一页就减掉一个 pageHeight,减到不大于 0 就停。

高度没超过一页,贴一次就够:

图片6.png

超页时,每一页贴的都是同一张 pageData,变的是 position。第一页 position 是 0,看见图的顶部;第二页减掉 841.89,变成 -841.89,图往上挪一页的高度,这一页露出的是下一段。PDF 每页只显示落在纸张里的那一截,上面挪出去的部分被裁掉。

leftHeight 减完如果还有剩余,才 addPage()。最后一页刚好贴完还去加一页,就会多一张空白页。这坑我见过不止一次。

这套切法按像素高度切,不会避开段落。某一行字正好压在页缝上,就会上半截在这一页、下半截在下一页。要避缝得自己按块截,或换成按 DOM 分页的方案;本例是整块截图再切,接进项目前先用真实内容打一份看看页缝。

pdf.save(PDF_FILE_NAME) 会弹出下载。本例文件名写成 content.pdf,业务里换成订单号、合同号更清楚。

怎么嵌进业务页,只导出指定容器

接入项目时:页面加上导出按钮和一块内容容器,业务脚本用下面这份完整示例。截图函数不用改,只是 getElementById 换成业务里那块节点。jQuery 这套建议用 Layui 的 layer.msg 做提示,具体按实际项目来定。

按钮放在容器外面。详情是 ajax 拉的,等 success 把 HTML 填进容器之后再允许点导出,别在空容器上截,导出一份空白页只能干瞪眼。

接入业务页的完整示例

四个库还按原来的顺序引。接口地址和 res.code 换成项目里的约定。内容区的 DOM 按业务来,只要保证截取节点有一个稳定的 id。

图片7.png

图片8.png

html2canvas 是异步的。一点击就 prop("disabled", true),结束再解开。不锁的话狂点会叠多个导出任务,弹一堆下载框。事件绑在 $("body").on("click", "#renderPdf", ...) 上,按钮是后来渲染出来的也能点。

只要内容在 #pdfContent 里,侧栏、顶栏、操作按钮都不会进 PDF。容器换成 #contract、#reportBox 也行,exportPdf 的入参跟着改。

纸张规格、页边距和文件名改哪里

1、纸张:本例 A4_WIDTH = 595.28、A4_HEIGHT = 841.89,new jsPDF("", "pt", "a4") 第三个参数也是 a4。要换成 A3,三个地方一起改,只改宽高、纸型还写 a4,页面对不齐。

2、方向:第一个参数空字符串是纵向。改成 "l" 是横向,A4_WIDTH 和 A4_HEIGHT 要对调。

3、页边距:PDF_MARGIN 本例 20。左右对称,图宽用 A4_WIDTH - PDF_MARGIN * 2。边距加大,图会变窄、页数变多。

4、文件名:PDF_FILE_NAME 本例 content.pdf。业务里拼订单号,例如 "order-" + id + ".pdf"。

5、清晰度:canvas.toDataURL("image/jpeg", 1.0) 第二个参数是 JPEG 质量,1.0 最清楚、文件也最大。要压体积改成 0.8。

6、截哪块:exportPdf("pdfContent") 换成业务容器的 id。

接入时这几处别漏:四个脚本顺序;按钮在截取节点外面;position 按 A4_HEIGHT 递减、leftHeight 按 pageHeight 递减,两套单位别写反;导出过程把按钮 disabled 掉。


案例下载
下载demo本地运行
经验分享