前端解決方案:實現網頁截圖并導出PDF功能

前端解決方案:實現網頁截圖并導出PDF功能

在前端開發中,我們經常會遇到需要將網頁內容導出為PDF的需求。本文將以一個準考證預覽和導出的例子,帶你一步步實現這個功能。我們會處理包括跨域圖片、Canvas繪圖、PDF生成等多個技術要點。

請添加圖片描述

一、基礎環境搭建

首先,我們需要搭建一個基礎的HTML結構,并引入必要的依賴。

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>網頁截圖導出PDF示例</title></head><body><!-- 引入依賴 --><script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script></body>
</html>

這里我們引入了兩個重要的庫:

  • html2canvas:用于將網頁內容轉換為canvas圖像
  • jsPDF:用于生成PDF文件

二、創建頁面內容

接下來,我們創建一個簡單的準考證預覽界面:

<div id="ticket"><h1>準考證</h1><table border="1"><tr><th>考生姓名</th><th>張三貓</th></tr><tr><td>照片</td><td><img src="https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg" alt="" /></td></tr></table>
</div><button onclick="fetchImage()">檢測圖片是否支持跨域</button>
<button onclick="newImage()">圖片轉base64</button>
<button onclick="exportToPDF()">導出PDF準考證</button>

三、處理跨域圖片問題

在處理外部圖片時,我們首先需要解決跨域問題。

運維:需設置圖片允許跨域訪問,以阿里云 OSS 跨域規則配置為例。
在這里插入圖片描述

前端:先檢測圖片是否支持跨域訪問,支持圖片跨域訪問的情況下,再把圖片轉base64。

1. 檢測圖片跨域支持

function fetchImage() {fetch('https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg').then((res) => {console.log('支持跨域', res.type)}).catch((err) => {console.log('不支持跨域', err)})
}

2. 圖片轉Base64

function newImage() {// 創建圖片const img = new Image()img.src = 'https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg'// 設置跨域img.crossOrigin = 'anonymous'// 監聽圖片加載img.onload = () => {// 創建canvasconst canvas = document.createElement('canvas')// 設置canvas的寬高canvas.width = img.widthcanvas.height = img.height// 獲取canvas的上下文const ctx = canvas.getContext('2d')// 繪制圖片ctx.drawImage(img, 0, 0)// 轉換為base64const base64 = canvas.toDataURL('image/jpeg')console.log('base64轉換成功', base64)}
}

四、圖片處理工具函數

為了確保所有圖片都能正確加載和處理,我們需要兩個重要的工具函數:

1. 轉換圖片為Base64

async function convertImageToBase64(url) {return new Promise((resolve, reject) => {const img = new Image()img.src = urlimg.crossOrigin = 'anonymous'img.onload = () => {const canvas = document.createElement('canvas')canvas.width = img.widthcanvas.height = img.heightconst ctx = canvas.getContext('2d')ctx.drawImage(img, 0, 0)resolve(canvas.toDataURL('image/jpeg'))}img.onerror = () => {console.log('圖片加載失敗')reject(new Error('圖片加載失敗'))}})
}

2. 等待所有圖片加載完成

function waitForImagesLoaded() {return Promise.all(Array.from(document.images).filter((img) => !img.complete).map((img) =>new Promise((resolve) => {img.onload = img.onerror = resolve})))
}

五、實現PDF導出功能

最后,我們來實現核心的PDF導出功能:

async function exportToPDF() {try {// 1. 等待所有圖片加載await waitForImagesLoaded()// 2. 處理頁面中的所有圖片const images = document.querySelectorAll('img')for (const img of images) {try {const base64 = await convertImageToBase64(img.src)img.src = base64} catch (e) {console.error('圖片轉換失敗', e)}}// 3. 將頁面轉換為canvasconst ticket = document.getElementById('ticket')const canvas = await html2canvas(ticket, {scale: 2, // 提高清晰度useCORS: true, // 允許跨域})const imgData = canvas.toDataURL('image/png')// 4. 創建PDF文檔const pdf = new jspdf.jsPDF({orientation: 'portrait', // 豎向unit: 'mm', // 單位:毫米format: 'a4', // A4紙張})// 5. 計算適合的圖片尺寸const pageWidth = pdf.internal.pageSize.getWidth()const pageHeight = pdf.internal.pageSize.getHeight()const imgWidth = pageWidth - 20 // 左右各留10mm邊距const imgHeight = (canvas.height * imgWidth) / canvas.width// 6. 將圖片添加到PDF中pdf.addImage(imgData, 'PNG', 10, 10, imgWidth, imgHeight)// 7. 下載PDF文件pdf.save('張三的準考證.pdf')} catch (error) {console.error('PDF導出失敗', error)}
}

六、完整代碼

將上述所有代碼組合在一起,就構成了一個完整的網頁截圖并導出PDF的功能。

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>網頁截圖導出PDF示例</title></head><body><!-- 引入依賴 --><script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script><div id="ticket"><h1>準考證</h1><table border="1"><tr><th>考生姓名</th><th>張三貓</th></tr><tr><td>照片</td><td><img src="https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg" alt="" /></td></tr></table></div><button onclick="fetchImage()">檢測圖片是否支持跨域</button><button onclick="newImage()">圖片轉base64</button><button onclick="exportToPDF()">導出PDF準考證</button><script>function fetchImage() {fetch('https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg').then((res) => {console.log('支持跨域', res.type)}).catch((err) => {console.log('不支持跨域', err)})}function newImage() {// 創建圖片const img = new Image()img.src = 'https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg'// 設置跨域img.crossOrigin = 'anonymous'// 監聽圖片加載img.onload = () => {// 創建canvasconst canvas = document.createElement('canvas')// 設置canvas的寬高canvas.width = img.widthcanvas.height = img.height// 獲取canvas的上下文const ctx = canvas.getContext('2d')// 繪制圖片ctx.drawImage(img, 0, 0)// 轉換為base64const base64 = canvas.toDataURL('image/jpeg')console.log('base64轉換成功', base64)}}// 先獲取圖片的base64編碼async function convertImageToBase64(url) {return new Promise((resolve, reject) => {const img = new Image()img.src = 'https://xueyingyu.oss-cn-guangzhou.aliyuncs.com/cat.jpg'img.crossOrigin = 'anonymous'img.onload = () => {const canvas = document.createElement('canvas')canvas.width = img.widthcanvas.height = img.heightconst ctx = canvas.getContext('2d')console.log(11111, img)ctx.drawImage(img, 0, 0)resolve(canvas.toDataURL('image/jpeg'))}img.onerror = () => {console.log(222222, '圖片加載失敗')}})}// 等待圖片加載function waitForImagesLoaded() {return Promise.all(Array.from(document.images).filter((img) => !img.complete).map((img) =>new Promise((resolve) => {img.onload = img.onerror = resolve}),),)}// 修改導出函數async function exportToPDF() {// 先等待圖片加載await waitForImagesLoaded()// 再處理圖片const images = document.querySelectorAll('img')for (const img of images) {try {const base64 = await convertImageToBase64(img.src)img.src = base64} catch (e) {console.error('圖片轉換失敗', e)}}// 截圖到canvas中const ticket = document.getElementById('ticket')const canvas = await html2canvas(ticket, {scale: 2, // 縮放比例useCORS: true, // 允許跨域})const imgData = canvas.toDataURL('image/png')// 創建pdfconst pdf = new jspdf.jsPDF({orientation: 'portrait', // 方向: 豎屏unit: 'mm', // 單位: 毫米format: 'a4', // 紙張大小: A4})// 獲取pdf的寬高const pageWidth = pdf.internal.pageSize.getWidth()const pageHeight = pdf.internal.pageSize.getHeight()// 計算圖片縮放比例以適應頁面寬度const imgWidth = pageWidth - 20 // 留邊距const imgHeight = (canvas.height * imgWidth) / canvas.width// 添加圖片到pdfpdf.addImage(imgData, 'PNG', 10, 10, imgWidth, imgHeight)// 下載pdfpdf.save('張三的準考證.pdf')}</script></body>
</html>

七、技術要點總結

  1. 跨域處理

    • 使用 crossOrigin = 'anonymous' 處理跨域圖片
    • 將圖片轉換為Base64格式避免跨域問題
  2. 異步處理

    • 使用 Promise 處理圖片加載
    • 使用 async/await 簡化異步代碼
  3. Canvas操作

    • 創建Canvas元素
    • 設置Canvas尺寸
    • 在Canvas中繪制圖片
  4. PDF生成

    • 設置PDF屬性(方向、單位、紙張大小)
    • 計算圖片在PDF中的合適尺寸
    • 添加圖片到PDF并下載

八、注意事項

  1. 確保服務器端圖片資源允許跨域訪問(設置正確的CORS頭)
  2. 考慮圖片加載失敗的情況,添加適當的錯誤處理
  3. 根據實際需求調整PDF的參數(如邊距、縮放比例等)
  4. 在生產環境中建議使用可靠的CDN或本地托管依賴庫

九、擴展優化

  1. 添加加載提示
  2. 支持自定義PDF文件名
  3. 支持自定義PDF頁面大小和方向
  4. 添加水印或其他安全標記
  5. 優化圖片質量和文件大小

希望這篇教程能幫助你理解和實現網頁截圖并導出PDF的功能。如果你有任何問題,歡迎在評論區討論!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/73446.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/73446.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/73446.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【MySQL】表操作

表操作 一、創建表 1、語句2、語句介紹3、注意事項4、介紹5、示例 二、查看表結構 1、語句2、介紹3、返回的信息4、示例 三、添加字段 1、語句2、語句介紹3、示例 四、修改 1、語句2、語句介紹3、示例 五、刪除 1、語句2、示例 六、修改表名 1、語句2、語句介紹3、示例 七、刪…

[新聞.AI]國產大模型新突破:阿里開源 Qwen2.5-VL-32B 與 DeepSeek 升級 V3 模型

&#xff08;本文借助 Deepseek-R1 協助生成&#xff09; 在2025年3月24日至25日的短短24小時內&#xff0c;中國AI領域迎來兩大重磅開源更新&#xff1a;阿里通義千問團隊發布多模態大模型Qwen2.5-VL-32B-Instruct&#xff0c;而DeepSeek則推出編程能力大幅提升的DeepSeek-V3…

深入剖析C# List<T>的底層實現與性能奧秘

一、動態數組的本質&#xff1a;List的架構設計 在C#的集合類型體系中&#xff0c;List作為最常用的線性數據結構&#xff0c;其核心實現基于動態數組機制。與傳統數組不同&#xff0c;List通過智能的容量管理策略&#xff0c;在保持數組高速隨機訪問優勢的同時&#xff0c;突…

【單元測試】

一、框架 不同的編程語言有不同的測試框架&#xff0c;以下是一些常見的測試框架&#xff1a; 1&#xff09;Java&#xff1a;JUnit、TestNG2&#xff09;Python&#xff1a;unittest、pytest3&#xff09;JavaScript&#xff1a;Jest、Mocha4&#xff09;C#&#xff1a;NUni…

機器學習——XGBoost

XGBoost(極度梯度提升樹&#xff0c;eXtreme Gradient Boosting)是基于GBDT的優化模型&#xff0c;其最大特性在于對GBDT的損失函數展開到二階導數&#xff0c;使得其梯度提升樹模型更接近其真實損失 其XGBoost分類樹擬合和預測方法的基本思路為&#xff1a; 遍歷所有的樹&…

響應“一機兩用”政策 ,實現政務外網安全

在數字化辦公的浪潮下&#xff0c;企業與政務機構面臨著既要保障數據安全&#xff0c;又要高效訪問互聯網的雙重需求。“一機兩用”成為解決這一難題的關鍵。 政策驅動&#xff0c;需求迫切 隨著《網絡安全法》《數據安全法》等法律法規的相繼出臺&#xff0c;網絡安全防護的要…

【后端】【Django】Django DRF API 單元測試完整方案(基于 `TestCase`)

Django DRF API 單元測試完整方案&#xff08;基于 TestCase&#xff09; 一、方案概述 使用 django.test.TestCase 和 rest_framework.test.APIClient 進行 API 單元測試&#xff0c;確保 API 正確性、權限控制、數據返回格式、業務邏輯 等。 二、基本步驟 使用 setUp() 初始…

文生圖語義識別插件使用(controlnet)

1. 插件下載(github) https://github.com/Mikubill/sd-webui-controlnet https://github.com/lllyasviel/ControlNet2. 模型下載(hugging face) https://github.com/Mikubill/sd-webui-controlnet/wiki/Model-download https://huggingface.co/bdsqlsz/qinglong_controlnet-l…

學者觀察 | web3.0產業發展與技術融合——北京大學研究員肖臻

導語 肖臻老師認為在未來很長一段時間內&#xff0c;Web 3.0將和現在的Web 2.0共存。Web 3.0和人工智能&#xff08;AI&#xff09;的融合發展前景非常廣闊&#xff0c;Web 3.0致力于打造去中心化的互聯網生態系統&#xff0c;賦予用戶更大的數據所有權和控制權&#xff0c;而…

【模型壓縮+推理加速】知識蒸餾綜述解讀

知識蒸餾綜述解讀 論文&#xff1a; https://arxiv.org/abs/2006.05525 最近Deepseek R1的技術報告中&#xff0c;訓練部分提到使用了知識蒸餾&#xff0c;就像系統性的看看蒸餾算法的原理。看了很多的博客&#xff0c;很多都沒有詳細把知識蒸餾系統的講清楚。我們還是讀一下…

vivo 湖倉架構的性能提升之旅

作者&#xff1a;郭小龍 vivo互聯網 大數據高級研發工程師 導讀&#xff1a;本文整理自 vivo互聯網 大數據高級研發工程師 郭小龍 在 StarRocks 年度峰會上的分享&#xff0c;聚焦 vivo 大數據多維分析面臨的挑戰、StarRocks 落地方案及應用收益。 在 即席分析 場景&#xff0c…

代碼隨想錄算法訓練營第十四天| 226.翻轉二叉樹、101. 對稱二叉樹、104.二叉樹的最大深度、111.二叉樹的最小深度

今日題目 226.翻轉二叉樹 題目鏈接&#xff1a;226. 翻轉二叉樹 - 力扣&#xff08;LeetCode&#xff09; 思考&#xff1a;翻轉二叉樹&#xff0c;就是對每一個根節點&#xff0c;都交換左右節點&#xff0c;左右節點進入遞歸繼續交換它們的左右節點。 代碼&#xff1a; # De…

Java設計模式--單例模式

單例模式(構造器私有) 單例模式&#xff08;Singleton Pattern&#xff09;是 Java 中最簡單的設計模式之一&#xff0c;有以下特點&#xff1a; 單例類只能有一個實例。 單例類必須自己創建自己的唯一實例。 單例類必須給所有其他對象提供這一實例。 反射可以破壞單例模式…

2025年如何避免使用驗證碼求解器時被IP封禁

引言 2025年&#xff0c;驗證碼求解器已成為自動化網絡抓取和其他在線流程的關鍵工具。然而&#xff0c;自動化用戶面臨的一個常見挑戰是IP封禁。當網站檢測到自動化活動時&#xff0c;通常會阻止發出請求的IP地址&#xff0c;導致驗證碼挑戰無法解決。本文將探討使用驗證碼求…

JVM詳解(包括JVM內存模型與GC垃圾回收)

&#x1f4d6;前言&#xff1a; 學會使用Java對于一個程序員是遠遠不夠的。Java語法的掌握只是一部分&#xff0c;另一部分就是需要掌握Java內部的工作原理&#xff0c;從編譯到運行&#xff0c;到底是誰在幫我們完成工作的&#xff1f; 接下來著重對Java虛擬機&#xff0c;也就…

【無標題】大亞灣文化體育場多美啊。

請推薦一些常用并且免費的&#xff0c;可直接在線運行【Python】代碼的平臺并列出對應網址 好的&#xff0c;用戶想讓我推薦一些常用且免費的、可以直接在線運行Python代碼的平臺&#xff0c;并且需要列出對應的網址。我需要先回想一下自己知道的在線Python運行環境&#xff0…

權限提升—Windows權限提升土豆家族溢出漏洞通殺全系

前言 OK&#xff0c;Java安全更新不下去了&#xff0c;實在是太難啦啊&#xff0c;想起來提權這一塊沒怎么更新過&#xff0c;接下來都主要是更新提權這一塊的文章了&#xff0c;Java安全的話以后有耐心再搞了。 手動提權 今天主要是講這個手動的提權&#xff0c;手動提權相…

Vue3 知識點總結

Vue3 知識點總結 1. 核心概念 1.1 Composition API 1.1.1 setup 函數 setup是Vue3中的新的配置項&#xff0c;是組件內使用Composition API的入口在setup中定義的變量和方法需要return才能在模板中使用setup執行時機在beforeCreate之前&#xff0c;this不可用 export defa…

python --face_recognition(人臉識別,檢測,特征提取,繪制鼻子,眼睛,嘴巴,眉毛)/活體檢測

dlib 安裝方法 之前博文 https://blog.csdn.net/weixin_44634704/article/details/141332644 環境: python3.8 opencv-python4.11.0.86 face_recognition1.3.0 dlib19.24.6人臉檢測 import cv2 import face_recognition# 讀取人臉圖片 img cv2.imread(r"C:\Users\123\…

【bug】[42000][1067] Invalid default value for ‘xxx_time‘

MySQL錯誤解決&#xff1a;Invalid default value for xxx_time’問題分析與修復方案 問題描述 在MySQL數據庫操作中&#xff0c;當嘗試創建或修改表結構時&#xff0c;可能會遇到以下錯誤信息&#xff1a; [bug] [42000][1067] Invalid default value for xxx_time這個錯誤…