pdf文件分页按需查看

pdf预览本来打算粗暴点,一次性查看全部,但是一个pdf四五百页导致手机端查看超出内存直接崩掉,崩掉会导致页面疯狂刷新,所以不得不进行优化
解决思路大致如下:

  1. canvas转为blob格式以图片的形式加载在页面(Blob URL 是基于磁盘的临时文件,可以减少内存占用)
  2. 分段按需加载,根据页面滑动位置决定加载哪页数据
  3. 历史pdf加载数据缓存,避免一直调用获取pdf逻辑,但不可缓存全部历史数据,变量数据过多也会导致崩掉
  4. 及时移除画布和图片,确保内存被释放

具体实现看代码吧!对了,我这里用的是vue3框架,方案大致都差不多,可供参考

  1. 安装 pdfjs-dist包(版本为4.10.38)
    npm install pdfjs-dist
  2. 使用
<template><div class="pdf-box"><van-loading v-if="pdfLoading && !isHasFirstPage" size="24px">内容正在玩命加载中,请稍后...</van-loading><div :style="(pdfLoading && !isHasFirstPage)?'position:fixed;transform: translateX(-200%);':''"><div v-for="page in pdfPages" :id="'__pdf_canvas_page_' + page" :key="page" /></div><van-loading v-if="pdfItemLoading && isHasFirstPage" size="24px" style="padding-top:0">滑慢点~,小的加载不过来啦...</van-loading></div>
</template><script setup>
import { ref, onMounted, nextTick, onUnmounted } from 'vue'
import * as pdfjsLib from 'pdfjs-dist'
import Worker from 'pdfjs-dist/build/pdf.worker.min.mjs?worker'const pdfjsWorker = new Worker()
let pdf = null
const pdfPages = ref(0) // pdf总页数
const pdfLoading = ref(true) // pdf 总页数获取loading
let cachedPages = new Map() // pdf历史 图片信息缓存
const pdfItemLoading = ref(false) // pdf 单独页面加载
const MAX_CACHED_PAGES = 6 // 最大缓存页面数
const lastScrollY = ref(0) // 最后一次滚动位置
const scrollDirection = ref('') // 页面滚动方向 up:上 down:下
const isHasFirstPage = ref(false) // 第一页是否已加载出来// 在组件挂载后加载 PDF
onMounted(() => {const pdfUrl = 'https://example.com/sample.pdf' // 替换为你的 PDF 文件 URLloadPdf(pdfUrl)window.addEventListener('scroll', handleScroll) // 滚动变化时更新条件
})
// 清理事件监听器
onUnmounted(() => {window.removeEventListener('scroll', handleScroll)if (pdf) {pdf.destroy()}
})
// 加载pdf
async function loadPdf(url) {pdfjsLib.GlobalWorkerOptions.workerPort = pdfjsWorkertry {const loadingTask = pdfjsLib.getDocument(url)pdf = await loadingTask.promisepdfPages.value = Number(pdf.numPages)pdfLoading.value = falseawait nextTick(() => {handleScroll()})} catch (err) {pdfLoading.value = falseconsole.error(err)}
}
// 渲染指定页面pdf
const renderPage = (pageNumber) => {return new Promise(() => {(async () => {const page = await pdf.getPage(pageNumber)const viewport = page.getViewport({ scale: 1.5 })const canvas = document.createElement('canvas')const ctx = canvas.getContext('2d')canvas.height = viewport.heightcanvas.width = viewport.widthawait page.render({ canvasContext: ctx, viewport }).promise// 将画布内容缓存为图片const blobURL = await canvasToBlobURL(canvas)const image = new Image()image.src = blobURL// 将画布内容转换为 Blob URLimage.id = `page-image-${pageNumber}`image.style.width = '100%'// 将图片添加到容器中const container = document.getElementById(`__pdf_canvas_page_${pageNumber}`)container.innerHTML = ''container.appendChild(image)// 缓存 Blob URL 和 Image 标签cachePage(pageNumber, { blobURL, image: image.src })pdfItemLoading.value = false// resolve()})()})
}
// 缓存页面
const cachePage = (pageNumber, data) => {cachedPages.set(pageNumber, data)if (cachedPages.size > 0) { // 第一页是否已加载出来isHasFirstPage.value = true}if (cachedPages.size > MAX_CACHED_PAGES) {// 如果缓存数量超过限制cachedPages = new Map([...cachedPages.entries()].sort((a, b) => a[0] - b[0])) // 排序let oldestPage = ''if (scrollDirection.value === 'up') { // 往上滑动 移除最后的页面const keysArray = [...cachedPages.entries()]oldestPage = keysArray[keysArray.length - 1][0]} else { // 往上滑动 移除最早的页面oldestPage = cachedPages.keys().next().value}if (oldestPage && pageNumber !== oldestPage) {unloadPage(oldestPage)}}
}
// 将画布内容转换为 Blob URL
const canvasToBlobURL = (canvas) => {return new Promise((resolve) => {canvas.toBlob((blob) => {const url = URL.createObjectURL(blob)resolve(url)})})
}
// 清除非可视pdf信息
const unloadPage = (pageNumber) => {const container = document.getElementById(`__pdf_canvas_page_${pageNumber}`)const canvas = document.getElementById(`page-${pageNumber}`)const image = document.getElementById(`page-image-${pageNumber}`)const cachedData = cachedPages.get(pageNumber)if (cachedData) {const rect = container.getBoundingClientRect()container.innerHTML = ''const div = document.createElement('div')div.style.height = rect.height + 'px'container.appendChild(div)if (cachedData.blobURL) {URL.revokeObjectURL(cachedData.blobURL)} // 释放 Blob URL}if (canvas) {canvas.remove()} // 移除画布if (image) {image.remove()} // 移除图片// 从缓存中移除页面cachedPages.delete(pageNumber)
}
// 处理滚动事件
function handleScroll() {// pdfconst windowHeight = window.innerHeightconst currentScrollY = window.scrollY// 判断往上滑动还是往下scrollDirection.value = currentScrollY > lastScrollY.value ? 'down' : 'up'lastScrollY.value = currentScrollYconst pdfPagesArr = Array.from({ length: pdfPages.value }, (_, i) => i + 1)pdfPagesArr.reduce((accumulatorPromise, next) => {return accumulatorPromise.then(() => {	// 上一个接口执行完毕再执行下一个const pageElement = document.getElementById('__pdf_canvas_page_' + next)if (pageElement) {const rect = pageElement.getBoundingClientRect()if (!pdfItemLoading.value) {if ((scrollDirection.value === 'up' ? rect.top < windowHeight + 200 : rect.top < windowHeight) && rect.bottom > 0) {if (cachedPages.has(next)) { // 已加载则跳过return}pdfItemLoading.value = truereturn renderPage(next) // 如果页面未加载,则加载该页}}}})}, Promise.resolve())
}
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/37232.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[数据结构]排序之 归并排序(有详细的递归图解)

一、非递归 基本思想&#xff1a; 归并排序&#xff08; MERGE-SORT &#xff09;是建立在归并操作上的一种有效的排序算法 , 该算法是采用分治法&#xff08; Divide andConquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#x…

在本地跑通spark环境

官网下载spark 下载spark 解压就好 本地配置环境变量 配置环境变量&#xff08;系统环境变量&#xff09; 新增 SPARK_HOME 变量名&#xff1a;SPARK_HOME 变量值&#xff1a;F:\class\spark\Spark_env\spark-3.4.4-bin-hadoop3 配置 PATH&#xff0c;新增如下&#xff1a…

UE5材质法线强度控制节点FlattenNormal

连法 FlattenNormal内部是这样的 FlattenNormal的作用是用来调整法线强度 连上FlattenNormal后 拉高数值

Appium使用文档

Appium旨在支持许多不同平台&#xff08;移动端、网页端、桌面端等&#xff09;的UI自动化。不仅如此&#xff0c;它还旨在支持用不同语言&#xff08;JS、Java、Python等&#xff09;编写的自动化代码。 1. 环境搭建 资源下载&#xff1a; 链接: https://pan.baidu.com/s/1K5Q…

Python绘图技巧,主流绘图库

一、主流绘图库概览 1. 核心工具对比 库名称特点适用场景Matplotlib基础绘图库&#xff0c;高度可定制科学绘图、论文图表Seaborn基于Matplotlib&#xff0c;统计图表优化数据分布、关系可视化Plotly交互式可视化&#xff0c;支持网页输出仪表盘、动态数据展示Pandas内置简易…

使用LLM自动化生成微电网Simulink模型

&#x1f680; 使用LLM自动化生成微电网Simulink模型&#xff01;⚡ 在构建微电网仿真模型时&#xff0c;我们通常需要手动拖拽模块、设置参数&#xff0c;耗费大量时间。现在&#xff0c;通过结合LLM&#xff08;如 GPT-4&#xff09;与 MATLAB 脚本&#xff0c;我们可以自动…

Git常用操作之GitLab

Git常用操作之GitLab 小薛博客官网&#xff1a;小薛博客Git常用操作之GitLab官方地址 1、GitLab安装 https://gitlab.cn/install/ 1、Docker安装GitLab https://docs.gitlab.cn/jh/install/docker.html 1、设置卷位置 在设置其他所有内容之前&#xff0c;请配置一个新的…

【AI】AI编程助手:Cursor、Codeium、GitHub Copilot、Roo Cline、Tabnine

文章目录 一、基本特性对比二、收费标准三、私有部署能力1、Tabnine2、Roo Code 三、代码补全与自然语言生成代码四、安装独立的IDE安装插件安装 五、基本使用&#xff08;一&#xff09;Cursor&#xff08;二&#xff09;GitHub Copilot1、获取代码建议2.聊天1&#xff09;上下…

[贪心算法]买卖股票的最佳时机 买卖股票的最佳时机Ⅱ K次取反后最大化的数组和 按身高排序 优势洗牌(田忌赛马)

1.买卖股票的最佳时机 暴力解法就是两层循环&#xff0c;找出两个差值最大的即可。 优化&#xff1a;在找最小的时候不用每次都循环一遍&#xff0c;只要在i向后走的时候&#xff0c;每次记录一下最小的值即可 class Solution { public:int maxProfit(vector<int>& p…

康谋方案 | AVM合成数据仿真验证方案

随着自动驾驶技术的快速发展&#xff0c;仿真软件在开发过程中扮演着越来越重要的角色。仿真传感器与环境不仅能够加速算法验证&#xff0c;还能在安全可控的条件下进行复杂场景的重复测试。 本文将分享如何利用自动驾驶仿真软件配置仿真传感器与搭建仿真环境&#xff0c;并对…

Django Rest Framework 创建纯净版Django项目部署DRF

描述创建纯净版的Django项目和 Django Rest Framework 环境的部署 一、创建Django项目 1. 环境说明 操作系统 Windows11python版本 3.9.13Django版本 V4.2.202. 操作步骤(在Pycharm中操作) 创建Python项目drfStudy、虚拟环境 ​虚拟环境中安装 jdangopip install django==4.…

数据结构篇——二叉树的存储与遍历

一、引入 书接上文&#xff0c;文于此续。上文我们学到了树的存储结构&#xff0c;那么今天&#xff0c;我们来学习下几种特殊的二叉树以及关于它的各种遍历&#xff0c;让我们一起加油吧。 二、特殊的二叉树 二叉树的特殊形式这里介绍3种&#xff0c;其中需要着重记忆的有…

Vulnhub-wordpress通关攻略

姿势一、后台修改模板拿WebShell 第一步&#xff1a;进⼊Vulhub靶场并执⾏以下命令开启靶场&#xff1b;在浏览器中访问并安装好.... 第二步&#xff1a;找到外观--编辑--404.php&#xff0c;将原内容删除并修改为一句话木马&#xff0c;点击更新--File edited successfully. &…

「清华大学、北京大学」DeepSeek 课件PPT专栏

你要的 这里都打包好啦&#xff0c;快快收藏起来&#xff01; 名称 链接 团队简介 类型 DeepSeek——从入门到精通 1️⃣ DeepSeek从入门到精通「清华团队」 清华大学新闻与传播学院 新媒体研究中心 元宇宙文化实验室 PPT课件 DeepSeek如何赋能职场应用? ——从提示语…

【docker】--- 详解 WSL2 中的 Ubuntu 和 Docker Desktop 的区别和关系!

在编程的艺术世界里,代码和灵感需要寻找到最佳的交融点,才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里,我们将共同追寻这种完美结合,为未来的世界留下属于我们的独特印记。【WSL 】--- Windows11 迁移 WSL 超详细指南 —— 给室友换一个宿舍! 开发环境一、引…

【图像处理基石】什么是HDR图片?

1. 什么是HDR图片&#xff1f; HDR&#xff08;高动态范围图像&#xff0c;High Dynamic Range&#xff09;是一种通过技术手段扩展照片明暗细节的成像方式。以下是关于HDR的详细说明&#xff1a; 核心原理 动态范围&#xff1a;指图像中最亮和最暗区域之间的亮度差。人眼能…

HarmonyOS Next中的弹出框使用

HarmonyOS Next弹出框概述及分类 弹出框是一种模态窗口&#xff0c;通常用于在保持当前上下文环境的同时&#xff0c;临时展示用户需关注的信息或待处理的操作。用户需在模态弹出框内完成相关交互任务之后&#xff0c;才能退出模态模式。弹出框可以不与任何组件绑定&#xff0…

Java多线程与高并发专题——为何每次用完 ThreadLocal 都要调用 remove()?

什么是内存泄漏 首先&#xff0c;我们要知道这个事情和内存泄漏有关&#xff0c;所以就让我们先来看一下什么是内存泄漏。 内存泄漏指的是&#xff0c;当某一个对象不再有用的时候&#xff0c;占用的内存却不能被回收&#xff0c;这就叫作内存泄漏。 因为通常情况下&#xf…

视频推拉流EasyDSS点播平台云端录像播放异常的问题排查与解决

视频推拉流EasyDSS视频直播点播平台可提供一站式的视频转码、点播、直播、视频推拉流、播放H.265视频等服务&#xff0c;搭配RTMP高清摄像头使用&#xff0c;可将无人机设备的实时流推送到平台上&#xff0c;实现无人机视频推流直播、巡检等应用。 有用户反馈&#xff0c;项目现…

《笔记》Android 获取第三方应用及查看应用信息、apk大小、缓存、存储,以及第三方清除缓存

获取应用相关信息&#xff1a; PS:manifest标签中设置以下属性表示系统应用 android:process"system" android:sharedUserId"android.uid.system" //获取所有应用&#xff08;非系统apk&#xff0c;有些应用获取不到&#xff09; List<ApplicationInf…