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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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 小测试 …