🧑💻 寫在開頭
點贊 + 收藏 === 學會🤣🤣🤣
前端 HTML 轉 PDF 的工具函數,核心作用是:把網頁中指定 ID 的 DOM 元素(比如表格、報表、表單等),通過 html2canvas 和 jspdf 兩個庫轉換成 PDF 文件並下載到本地。
簡單説:它能讓用户 “一鍵下載” 網頁上的某個區域為 PDF(比如報表、數據統計頁、合同預覽頁等),還預留了 “水印功能” 的註釋代碼(可按需啓用)。
核心依賴説明
函數依賴兩個關鍵庫,必須先安裝才能使用:
函數核心邏輯(分步拆解)
函數名 htmlToPdf,接收兩個參數:
title:下載的 PDF 文件名(比如 “2024 年報表”);htmlId:需要轉換的 HTML 元素的 ID(比如#report-container)。
整體流程:定位 HTML 元素 → 滾動置頂避免截圖不全 → (可選加水印)→ HTML 轉 Canvas → Canvas 轉 PDF → 下載 PDF
1. 準備工作:定位元素 + 滾動置頂
const element = document.querySelector(htmlId); // 找到要轉PDF的HTML元素 // 滾動置頂:避免元素被滾動條遮擋,導致截圖不全 window.pageYOffset = 0; document.documentElement.scrollTop = 0; document.body.scrollTop = 0;
2. 可選:添加水印(已註釋,需啓用可取消註釋)
註釋部分的邏輯是:創建一個帶文字水印(比如 “我是水印”)的 Canvas,作為背景圖添加到目標元素上,轉 PDF 時水印會一起被截取(適合需要版權保護的場景)。
3. HTML 轉 Canvas(核心步驟)
html2Canvas(element, {
allowTaint: true, // 允許跨域圖片(如果元素內有跨域圖片需開啓)
useCORS: true, // 啓用CORS跨域支持
scale: 2, // 縮放2倍:提升PDF清晰度(代價是文件變大)
height: element.scrollHeight, // 用元素實際滾動高度:避免只截取可視區域(關鍵!)
windowHeight: element.scrollHeight
}).then(canvas => {
// Canvas生成成功後,進入PDF生成步驟
});
4. Canvas 轉 PDF 並下載
const contentWidth = canvas.width; // Canvas寬度
const contentHeight = canvas.height; // Canvas高度
const pageHeight = (contentWidth * 841.89) / 592.28; // A4紙的高度(按比例計算)
let leftHeight = contentHeight; // 未生成PDF的剩餘高度
let position = 0; // PDF頁面偏移量
// 創建A4尺寸的PDF(縱向:"p" = portrait)
const pdf = new JsPDF("p", "pt", "a4");
// Canvas轉成圖片數據(jpeg格式,質量1.0)
const pageData = canvas.toDataURL("image/jpeg", 1.0);
// 處理分頁:如果內容高度超過1頁A4紙,自動分頁
if (leftHeight < pageHeight) {
// 單頁:直接嵌入圖片(20是左右邊距,避免內容貼邊)
pdf.addImage(pageData, "JPEG", 20, 20, imgWidth, imgHeight);
} else {
// 多頁:循環截取內容,每滿1頁添加新頁面
while (leftHeight > 0) {
pdf.addImage(pageData, "JPEG", 20, position, imgWidth, imgHeight);
leftHeight -= pageHeight;
position -= 841.89; // 向下偏移A4紙高度
if (leftHeight > 0) pdf.addPage(); // 剩餘內容不為空時,新增一頁
}
}
// 觸發下載:文件名=title+".pdf"
pdf.save(title + ".pdf");
如何使用
1. 安裝依賴
# npm 安裝 npm install html2canvas jspdf --save # pnpm 安裝 pnpm add html2canvas jspdf
2.創建一個htmlToPdf.js文件
import html2Canvas from "html2canvas";
import JsPDF from "jspdf";
// title:下載文件的名稱 htmlId:包裹的標籤的id
const htmlToPdf = (title, htmlId) => {
const element = document.querySelector(htmlId);
window.pageYOffset = 0;
document.documentElement.scrollTop = 0;
document.body.scrollTop = 0;
setTimeout(() => {
// // 以下注釋的是增加導出的pdf水印 !!!!!!!!!!!!!
// const value = '我是水印'
// //創建一個畫布
// let can = document.createElement('canvas')
// //設置畫布的長寬
// can.width = 400
// can.height = 500
// let cans = can.getContext('2d') as any
// //旋轉角度
// cans.rotate((-15 * Math.PI) / 180)
// cans.font = '18px Vedana'
// //設置填充繪畫的顏色、漸變或者模式
// cans.fillStyle = 'rgba(200, 200, 200, 0.40)'
// //設置文本內容的當前對齊方式
// cans.textAlign = 'left'
// //設置在繪製文本時使用的當前文本基線
// cans.textBaseline = 'Middle'
// //在畫布上繪製填色的文本(輸出的文本,開始繪製文本的X座標位置,開始繪製文本的Y座標位置)
// cans.fillText(value, can.width / 8, can.height / 2)
// let div = document.createElement('div')
// div.style.pointerEvents = 'none'
// div.style.top = '20px'
// div.style.left = '-20px'
// div.style.position = 'fixed'
// div.style.zIndex = '100000'
// div.style.width = element.scrollHeight + 'px'
// div.style.height = element.scrollHeight + 'px'
// div.style.background =
// 'url(' + can.toDataURL('image/png') + ') left top repeat'
// element.appendChild(div) // 到頁面中
html2Canvas(element, {
allowTaint: true,
useCORS: true,
scale: 2, // 提升畫面質量,但是會增加文件大小
height: element.scrollHeight, // 需要注意,element的 高度 寬度一定要在這裏定義一下,不然會存在只下載了當前你能看到的頁面 避雷避雷!!!
windowHeight: element.scrollHeight
}).then(function (canvas) {
const contentWidth = canvas.width;
const contentHeight = canvas.height;
// 一頁pdf顯示html頁面生成的canvas高度;
const pageHeight = (contentWidth * 841.89) / 592.28;
// 未生成pdf的html頁面高度
let leftHeight = contentHeight;
// 頁面偏移
let position = 0;
// a4紙的尺寸[595.28,841.89],html頁面生成的canvas在pdf中圖片的寬高 //40是左右頁邊距
const imgWidth = 595.28 - 40;
const imgHeight = (592.28 / contentWidth) * contentHeight;
const pageData = canvas.toDataURL("image/jpeg", 1.0);
const pdf = new JsPDF("p", "pt", "a4");
// 有兩個高度需要區分,一個是html頁面的實際高度,和生成pdf的頁面高度(841.89)
// 當內容未超過pdf一頁顯示的範圍,無需分頁
if (leftHeight < pageHeight) {
pdf.addImage(pageData, "JPEG", 20, 20, imgWidth, imgHeight);
} else {
while (leftHeight > 0) {
pdf.addImage(pageData, "JPEG", 20, position, imgWidth, imgHeight);
leftHeight -= pageHeight;
position -= 841.89;
// 避免添加空白頁
if (leftHeight > 0) {
pdf.addPage();
}
}
}
pdf.save(title + ".pdf");
});
}, 1000);
};
export default htmlToPdf;
3. 在 Vue/React 中使用示例
<!-- Vue 示例:頁面中有一個要轉PDF的區域 -->
<template>
<div>
<!-- 要轉PDF的元素:必須有唯一ID -->
<div id="report-container">
<h1>2024年銷售報表</h1>
<table>...</table> <!-- 報表內容 -->
</div>
<!-- 下載按鈕 -->
<button @click="downloadPdf">下載PDF</button>
</div>
</template>
<script>
import htmlToPdf from '@/utils/htmlToPdf'; // 導入函數
export default {
methods: {
downloadPdf() {
// 調用函數:參數1=PDF文件名,參數2=目標元素ID(注意加#)
htmlToPdf('2024年銷售報表', '#report-container');
}
}
}
</script>
常見問題與優化
1. 截圖不全 / 內容缺失?
- 確保目標元素的
height/scrollHeight正確(函數已處理,但如果元素是動態渲染的,可能需要調整setTimeout延遲時間,比如從 1000ms 改為 2000ms); - 避免元素內有
position: fixed的內容(會被重複截取)。
2. 圖片跨域導致截圖空白?
- 確保
html2Canvas配置中allowTaint: true和useCORS: true已開啓; - 圖片服務器需配置 CORS 允許當前域名訪問。
3. PDF 清晰度太低?
- 增大
scale參數(比如改為 3),但會導致文件變大; - 把
toDataURL的質量參數從 1.0 保留(已最優)。
4. 想要啓用水印?
- 取消代碼中水印相關的註釋,修改
value為你的水印文字(比如 “內部資料”); - 調整
rotate旋轉角度、font字體大小、fillStyle透明度(0.4 為半透明)。