pdf.js在iOS移動端分頁加載優化方案(ios移動端反復刷新加載問題)

背景與問題

在iOS移動端加載大型PDF文件時,由于設備內存限制,經常遇到以下問題:

  • 內存不足導致頁面崩潰
  • 大文件加載緩慢
  • 頁面反復重新加載

##解決方案

采用PDF.js的分頁加載策略,實現按需加載當前可視頁面及相鄰頁面,減少內存占用。

核心實現代碼

let pdfDoc: pdf.PDFDocumentProxy;
let currentVisiblePage = 1;
let isScrolling = false;async function loadPdf(url: string) {try {// 先下載為 Blob(兼容 iOS 緩存)const blob = await fetch(url).then((res) => res.blob());const blobUrl = URL.createObjectURL(blob);const loadingTask = pdf.getDocument({url: blobUrl,disableAutoFetch: true,disableStream: true,disableRange: true,useSystemFonts: true,});pdfDoc = await loadingTask.promise;await loadVisiblePages();window.addEventListener("scroll", handleScroll, { passive: true });} catch (error) {console.error("PDF加載失敗:", error);}
}

關鍵技術點

1. 分頁加載策略

  • 初始化加載:僅加載第一頁
  • 滾動監聽:動態加載當前可視頁面
  • 預加載:同時加載當前頁后2頁,提升瀏覽體驗
async function loadVisiblePages() {if (!pdfDoc) return;const startPage = currentVisiblePage;const endPage = Math.min(pdfDoc.numPages, currentVisiblePage + 2);for (let i = startPage; i <= endPage; i++) {if (!document.getElementById(`the-canvas${i}`)) {await renderPage(i);}}
}

2. 滾動優化處理

  • 使用requestAnimationFrame優化滾動性能
  • 防抖處理避免頻繁計算
function handleScroll() {if (isScrolling) return;isScrolling = true;requestAnimationFrame(async () => {const newPage = calculateCurrentPage();if (newPage !== currentVisiblePage) {currentVisiblePage = newPage;await loadVisiblePages();}isScrolling = false;});
}

3. 頁面位置計算

基于視口中心點計算當前最接近的頁面:

function calculateCurrentPage(): number {const scrollPosition = window.scrollY || window.pageYOffset;const viewportCenter = scrollPosition + window.innerHeight / 2;let closestPage = currentVisiblePage;let minDistance = Infinity;canvases.forEach((canvas) => {const pageNum = parseInt(canvas.id.replace("the-canvas", ""));const rect = canvas.getBoundingClientRect();const pageCenter = (rect.top + rect.bottom) / 2 + scrollPosition;const distance = Math.abs(pageCenter - viewportCenter);if (distance < minDistance) {minDistance = distance;closestPage = pageNum;}});return Math.max(1, Math.min(closestPage, pdfDoc.numPages));
}

4. 內存管理

雖然注釋掉了卸載邏輯,但保留了卸載能力:

function unloadPage(pageNum: number) {const canvas = document.getElementById(`the-canvas${pageNum}`);if (canvas) {const page = (canvas as any)._pdfPage;if (page) {page.cleanup();page._destroy();}canvas.remove();}
}

性能優化措施

  1. PDF加載配置

    • disableAutoFetch: true - 禁用自動獲取
    • disableStream: true - 禁用流式加載
    • disableRange: true - 禁用范圍請求
    • useSystemFonts: true - 使用系統字體
  2. 渲染優化

    • 動態計算canvas尺寸適配屏幕
    • 使用CSS控制canvas顯示樣式
canvas.style.width = `${document.body.clientWidth}px`;
canvas.style.height = `${document.body.clientWidth / (canvas.width / canvas.height)}px`;

總結

該方案通過以下方式解決了iOS移動端PDF加載問題:

  • 分頁按需加載降低內存占用
  • 智能預加載提升用戶體驗
  • 優化的滾動計算確保流暢性
  • 完善的錯誤處理增強穩定性

對于超大PDF文件,可考慮進一步優化:

  1. 實現頁面卸載邏輯
  2. 添加LRU緩存策略
  3. 支持更精細的縮放級別控制

完整代碼

import * as pdf from 'pdfjs-dist';pdf.GlobalWorkerOptions.workerSrc = 'path/to/pdf.worker.js';let pdfDoc: pdf.PDFDocumentProxy;
let currentVisiblePage = 1;
let isScrolling = false;async function loadPdf(url: string) {try {// 先下載為 Blob(兼容 iOS 緩存)const blob = await fetch(url).then((res) => res.blob());const blobUrl = URL.createObjectURL(blob);const loadingTask = pdf.getDocument({url: blobUrl,disableAutoFetch: true,disableStream: true,disableRange: true,useSystemFonts: true,});pdfDoc = await loadingTask.promise;// 初始化加載第一頁await loadVisiblePages();// 添加滾動監聽window.addEventListener("scroll", handleScroll, { passive: true });} catch (error) {console.error("PDF加載失敗:", error);}
}function handleScroll() {if (isScrolling) return;isScrolling = true;requestAnimationFrame(async () => {const newPage = calculateCurrentPage();if (newPage !== currentVisiblePage) {currentVisiblePage = newPage;await loadVisiblePages();}isScrolling = false;});
}function calculateCurrentPage(): number {if (!pdfDoc || !document.getElementById("pdfViewerPages")) {return currentVisiblePage;}const scrollPosition = window.scrollY || window.pageYOffset;const pdfContainer = document.getElementById("pdfViewerPages")!;const containerTop = pdfContainer.offsetTop;const relativeScroll = scrollPosition - containerTop;const viewportCenter = relativeScroll + window.innerHeight / 2;const canvases = Array.from(document.querySelectorAll('canvas[id^="the-canvas"]'));// 找出距離視口中心最近的頁面let closestPage = currentVisiblePage;let minDistance = Infinity;canvases.forEach((canvas) => {const pageNum = parseInt(canvas.id.replace("the-canvas", ""));const rect = canvas.getBoundingClientRect();const pageTop = rect.top + scrollPosition - containerTop;const pageBottom = rect.bottom + scrollPosition - containerTop;const pageCenter = (pageTop + pageBottom) / 2;const distance = Math.abs(pageCenter - viewportCenter);if (distance < minDistance) {minDistance = distance;closestPage = pageNum;}});return Math.max(1, Math.min(closestPage, pdfDoc.numPages));
}async function loadVisiblePages() {if (!pdfDoc) return;// 加載可見頁(當前頁及后兩頁)const startPage = currentVisiblePage;const endPage = Math.min(pdfDoc.numPages, currentVisiblePage + 2);for (let i = startPage; i <= endPage; i++) {if (!document.getElementById(`the-canvas${i}`)) {try {await renderPage(i);} catch (error) {console.error(`渲染第 ${i} 頁失敗:`, error);}}}// 下載完成時,loading消失loading.value = false;
}async function renderPage(pageNum: number) {const page = await pdfDoc.getPage(pageNum);const canvas = document.createElement("canvas");canvas.id = `the-canvas${pageNum}`;canvas.className = "pdf-page";const scaledViewport = page.getViewport({ scale: 1 }); // 縮放后的視口canvas.height = Math.floor(scaledViewport.height); // 設置畫布的高度canvas.width = Math.floor(scaledViewport.width); // 設置畫布的寬度canvas.style.width = `${document.body.clientWidth}px`; // 設置畫布的寬度canvas.style.height = `${document.body.clientWidth / (canvas.width / canvas.height)}px`; // 設置畫布的高度// 設置canvas樣式Object.assign(canvas.style, {display: "block",margin: "10px auto",boxShadow: "0 2px 5px rgba(0,0,0,0.1)",});await page.render({canvasContext: canvas.getContext("2d")!,viewport: scaledViewport,}).promise;document.getElementById("pdfViewerPages")?.appendChild(canvas);(canvas as any)._pdfPage = page;
}function unloadPage(pageNum: number) {const canvas = document.getElementById(`the-canvas${pageNum}`);if (canvas) {const page = (canvas as any)._pdfPage;if (page) {try {page.cleanup();page._destroy();} catch (e) {console.warn(`卸載頁面 ${pageNum} 時出錯:`, e);}}canvas.remove();}
}

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

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

相關文章

【C++】來學習使用set和map吧

各位大佬好&#xff0c;我是落羽&#xff01;一個堅持不斷學習進步的大學生。 如果您覺得我的文章有所幫助&#xff0c;歡迎多多互三分享交流&#xff0c;一起學習進步&#xff01; 也歡迎關注我的blog主頁: 落羽的落羽 文章目錄 一、set和map是什么二、set系列1. set2. mult…

h5st逆向分析

h5st最新5.1版本逆向分析 申明定位h5st生成的位置動態插樁,事半功倍日志分析,十分鐘還原算法邏輯申明 本文僅用來記錄學習過程以免日后忘了,如有侵權請聯系刪除。 定位h5st生成的位置 通過關鍵字“sign”搜索,可以定位到window.PSign.sign(f)這個位置,f參數的值為{ &qu…

湖北理元理律師事務所企業債務優化路徑:司法重整中的再生之道

一、企業債務危機的核心矛盾&#xff1a;生存與清償的博弈 通過分析湖北理元理律師事務所經辦的17件企業債務案件&#xff0c;發現共性難題&#xff1a; 債權人要求立即清償 → 企業需持續經營造血 → 司法程序存在時間差 解決方案&#xff1a;構建“三重防火墻”機制 經…

鏈家Android面試題及參考答案

目錄 請詳細解釋類加載的過程,包括每一步的具體實現。并說明Android中的dex分包技術及其在熱更新中的應用 比較JVM和DVM的區別。在JVM中一個程序崩潰是否可能導致系統崩潰?DVM中呢? 請解釋網絡IP協議、TCP、UDP、HTTP、HTTPS、Socket的概念,并說明它們之間的區別 請深入…

LeetCode-多語言實現冒泡排序以及算法優化改進

目錄 一、冒泡排序算法 二、應用場景/前提條件 &#x1f308; 優點 &#x1f4e2; 缺點 三、經典算法實現并優化改進 方法一&#xff1a;記錄最后一次交換位置&#xff0c;下一輪只遍歷到該位置 方法二&#xff1a;添加標志位跟蹤是否發生交換&#xff0c;無交換則提前終…

JAVA畢業設計227—基于SpringBoot+hadoop+spark+Vue的大數據房屋維修系統(源代碼+數據庫)

畢設所有選題&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于SpringBoothadoopsparkVue的大數據房屋維修系統(源代碼數據庫)227 一、系統介紹 本項目前后端分離&#xff0c;分為業主、維修人員、管理員三種角色 1、業主&#xff1a; 登…

MADlib —— 基于 SQL 的數據挖掘解決方案(9)—— 數據探索之概率統計

目錄 一、概率 1. 概率的定義 2. 概率質量函數與概率密度函數 3. 條件概率 4. 期望值 二、MADlib 的概率相關函數 1. 函數語法 2. 示例 &#xff08;1&#xff09;求標準正態分布下&#xff0c;1 的概率密度函數 &#xff08;2&#xff09;求標準正態分布下&#xff…

耳蝸里的春天

早春的鄭州飄著細雨&#xff0c;我牽著女兒小滿的手走進市殘疾人康復中心時&#xff0c;玻璃門內突然傳來一陣清脆的笑聲。穿天藍色毛衣的小女孩戴著粉色耳蝸&#xff0c;正踮腳拍打著墻上的卡通貼畫&#xff0c;銀色的連接線在她耳后晃動&#xff0c;像一只折翼卻仍在起舞的蝴…

OCR(光學字符識別)算法

OCR&#xff08;光學字符識別&#xff09;算法在景區護照閱讀器中的應用是核心技術之一&#xff0c;它通過圖像處理和機器學習快速提取護照信息&#xff0c;顯著提升自動化水平。以下是其具體應用場景、技術實現及優化方向&#xff1a; 一、OCR在護照閱讀器中的核心作用 關鍵信…

html打印合同模板

概述&#xff08;吐槽&#xff09;&#xff1a;記錄一個html打印合同模板的功能&#xff0c;技術棧有點雜&#xff0c;千禧年出產老系統的數據庫是sqlserver2008&#xff0c;原系統框架是c#&#xff0c;無法二開&#xff0c;因為原系統的合同生成功能出現bug&#xff0c;沒有供…

DeepCritic: SFT+RL兩階段訓練突破LLM自我監督!顯著提升大模型的自我批判能力!!

摘要&#xff1a;隨著大型語言模型&#xff08;LLMs&#xff09;的迅速發展&#xff0c;對其輸出進行準確反饋和可擴展監督成為一個迫切且關鍵的問題。利用LLMs作為批評模型以實現自動化監督是一個有前景的解決方案。在本研究中&#xff0c;我們專注于研究并提升LLMs在數學批評…

【深度學習】深度學習中的張量:從多維數組到智能計算單元

? 一、n維數組&#xff08;張量&#xff0c;Tensor&#xff09; 1. 定義 張量&#xff08;Tensor&#xff09;是一個通用的n維數組數據結構。 它的維度&#xff08;維數&#xff09;決定了它的形狀&#xff0c;例如&#xff1a; 維度名稱舉例說明0維標量&#xff08;scalar…

以太網MDI信號PCB EMC設計要點

1. PHY側和RJ45連接器側通用MDI布局建議 1. MDI差分對保持對稱走線&#xff0c;走線上的焊盤封裝應一致&#xff0c;焊盤放置位置也應對稱。可以減少EMI測試中的模式轉換。 ??2. MDI走線應保持阻抗匹配&#xff0c;從而減少信號線上的反射。 ??3. MDI走線下需有連續完整的接…

深入淺出WebGL:在瀏覽器中解鎖3D世界的魔法鑰匙

WebGL&#xff1a;在瀏覽器中解鎖3D世界的魔法鑰匙 引言&#xff1a;網頁的邊界正在消失 在數字化浪潮的推動下&#xff0c;網頁早已不再是靜態信息的展示窗口。如今&#xff0c;我們可以在瀏覽器中體驗逼真的3D游戲、交互式數據可視化、虛擬實驗室&#xff0c;甚至沉浸式的V…

pysnmp模塊中 GET、SET、WALK操作詳細分步解析

1. SNMP GET 操作詳解 1.1 核心代碼結構 from pysnmp.hlapi import *# 定義參數 community public # SNMPv2c 社區名 target_ip 192.168.1.1 # 目標設備 IP oid 1.3.6.1.2.1.1.1.0 # 要查詢的 OID# 發起 GET 請求 error_indication, error_status, error_index, …

接收rabbitmq消息

以下是一個使用純Java&#xff08;非Spring Boot&#xff09;接收RabbitMQ消息的完整實現&#xff0c;包含Maven依賴和持續監聽消息的循環&#xff1a; 1. 首先添加Maven依賴 (pom.xml) <dependencies><!-- RabbitMQ Java Client --><dependency><group…

SQL進階之旅 Day 23:事務隔離級別與性能優化

【SQL進階之旅 Day 23】事務隔離級別與性能優化 文章簡述 在數據庫系統中&#xff0c;事務是確保數據一致性和完整性的核心機制。隨著業務復雜度的提升&#xff0c;如何合理設置事務隔離級別以平衡并發性能與數據一致性成為開發人員必須掌握的關鍵技能。本文深入解析事務隔離級…

六.原型模式

一.原型模式的定義 原型模式是一種創建型設計模式&#xff0c;通過復制現有對象&#xff08;原型&#xff09;生成新對象&#xff0c;避免重復初始化成本。需了解以下關鍵概念&#xff1a; ?淺拷貝?&#xff1a;復制基本類型字段&#xff0c;引用類型字段共享內存地址&#…

【筆記】LoRA 理論與實現|大模型輕量級微調

論文鏈接&#xff1a;LoRA: Low-Rank Adaptation of Large Language Models 官方實現&#xff1a;microsoft/LoRA 非官方實現&#xff1a;huggingface/peft、huggingface/diffusers 這篇文章要介紹的是一種大模型/擴散模型的微調方法&#xff0c;叫做低秩適應&#xff08;也就是…

Cilium動手實驗室: 精通之旅---15.Isovalent Enterprise for Cilium: Network Policies

Cilium動手實驗室: 精通之旅---15.Isovalent Enterprise for Cilium: Network Policies 1. 環境信息2. 測試環境部署3. 默認規則3.1 測試默認規則3.2 小測驗 4. 網絡策略可視化4.1 通過可視化創建策略4.2 小測試 5. 測試策略5.1 應用策略5.2 流量觀測5.3 Hubble觀測5.4 小測試 …