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.pswp.cn/news/898716.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/898716.shtml
英文地址,請注明出處:http://en.pswp.cn/news/898716.shtml

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

相關文章

算力100問?第92問:為什么各地熱衷建設算力中心?

目錄 1、宏觀分析 2、政府角度分析 3、投資者角度分析 在數字化浪潮中,各地對算力中心建設的熱情高漲,這一現象背后潛藏著諸多深層次的原因,涵蓋了經濟、科技、社會等多個維度,且彼此交織,共同驅動著這一發展趨勢。 1、宏觀分析 從經濟結構轉型的底層邏輯來看,全球經…

Redis 內存管理

Redis 內存管理 1. Redis 給緩存數據設置過期時間的作用 給緩存數據設置過期時間&#xff08;TTL, Time-To-Live&#xff09;有以下幾個重要作用&#xff1a; (1) 自動釋放內存 避免緩存數據無限增長&#xff0c;導致 Redis 內存溢出。例如&#xff0c;在 會話管理、短連接…

PyCharm中使用pip安裝PyTorch(從0開始僅需兩步)

無需 anaconda&#xff0c;只使用 pip 也可以在 PyCharm 集成環境中配置深度學習 PyTorch。 本文全部信息及示范來自 PyTorch 官網。 以防你是super小白&#xff1a; PyCharm 中的命令是在 Python Console 中運行&#xff0c;界面左下角豎排圖標第一個。 1. 安裝前置包 numpy …

掌握新編程語言的秘訣:利用 AI 快速上手 Python、Go、Java 和 Rust

網羅開發 &#xff08;小紅書、快手、視頻號同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企業從事人工智能項目研發管理工作&#xff0c;平時熱衷于分享各種編程領域的軟硬技能知識以及前沿技術&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

如何理解java中Stream流?

在Java中&#xff0c;Stream 是 Java 8 引入的一個強大API&#xff0c;用于處理集合&#xff08;如 List、Set、Map 等&#xff09;數據的流式操作。它提供了一種聲明式、函數式的編程風格&#xff0c;可以高效地進行過濾、映射、排序、聚合等操作。 Stream 的核心概念 流&…

【Vitis AIE】FPGA快速部署ConvNet 示例MNIST數據集

AIE-ML 上的 MNIST ConvNet 版本&#xff1a;Vitis 2024.2 簡介 本教程在 AMD VersalTM 自適應 SoC AIE-ML 上實現了一個卷積神經網絡分類器&#xff0c;用于識別來自 MNIST 數據庫 的手寫數字。目標是說明如何將一個簡單的機器學習示例分區和向量化到 Versal AI 引擎。MNIS…

ubuntu桌面圖標異常——主目錄下的所有文件(如文檔、下載等)全部顯示在桌面

ubuntu桌面圖標異常 問題現象問題根源系統級解決方案方法一:全局修改(推薦多用戶環境)方法二:單用戶修改(推薦個人環境)操作驗證與調試避坑指南擴展知識參考文檔問題現象 主目錄文件異常顯示 用戶主目錄(如/home/user/)下的所有文件(如文檔、下載等)全部顯示在桌面,…

OceanBase 4.3.3 AP 解析:應用 RoaringBitmaps 類型處理海量數據的判重和基數統計

對于大數據開發人員而言&#xff0c;處理海量數據的判重操作和基數統計是常見需求&#xff0c;而 RoaringBitmap類型及其相關函數是當前非常高效的一種解決方案&#xff0c;許多大數據庫產品已支持RoaringBitmap類型。OceanBase 4.3.3版本&#xff0c;作為專為OLAP場景設計的正…

W25Qxx

概述 FLASH FLASH是一種是非易失性存儲器&#xff0c;即掉電后不會丟失數據&#xff0c;這和RAM&#xff08;隨機存儲器&#xff09;不同。 FLASH比起同作用的EEPROM有價格低的優點 FLASH的擦除操作是以扇區為單位的&#xff08;比起EEPROM來說操作較為不方便&#xff09; 芯片…

(滑動窗口)算法訓練篇11--力扣3.無重復字符的最長字串(難度中等)

目錄 1.題目鏈接&#xff1a;3.無重復字符的最長字符 2.題目描述&#xff1a; 3.解法(滑動窗口)&#xff1a; 1.題目鏈接&#xff1a;3.無重復字符的最長字符 2.題目描述&#xff1a; 給定一個字符串 s &#xff0c;請你找出其中不含有重復字符的 最長 子串 的長度。 示例…

深度學習1—Python基礎

深度學習1—python基礎 你的第一個程序 print(hello world and hello deep learning!)基本數據結構 空值 (None)&#xff1a;在 Python 中&#xff0c;None 是一個特殊的對象&#xff0c;用于表示空值或缺失的值。它不同于數字 0&#xff0c;因為 0 是一個有意義的數字&#…

記一次MyBatis分頁莫名其妙的失效,首次執行合適,后續執行分頁失效且異常

代碼幾乎一樣&#xff0c;為啥這個xml配置的就會出現莫名其妙的問題呢 org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.type.TypeException: Could not set parameters for mapping: ParameterMapping{propertymybatis_plus_first, modeI…

網絡不可達

導致此問題原因較多&#xff0c;我只針對一種情況進行討論&#xff0c;如果和文中癥狀不同&#xff0c;另尋他處&#xff0c;或者死馬當活馬醫&#xff08;&#xff1f;&#xff09; 如需轉載&#xff0c;標記出處 癥狀&#xff1a; 1.ping命令網絡不可達 2.ifconfig中網卡en…

【AI News | 20250322】每日AI進展

AI Repos 1、DeTikZify 可以把草圖或圖形轉換成TikZ代碼的模型&#xff0c;可用來繪制復雜的科學圖表&#xff0c;輸入草圖或文字描述即可轉換成TikZ代碼。DeTikZify強大的地方在于它能理解圖表的語義信息&#xff0c; 能識別圖表中的不同組成部分及其含義&#xff0c;比如坐標…

Debian12生產環境配置筆記

在 Debian 12 上進行生產環境配置的詳細步驟&#xff0c;涵蓋軟件更新、基礎軟件安裝、Docker 及 Redis 部署&#xff0c;以及 Nginx 配置多個虛擬主機等內容。所有命令均以 root 用戶身份執行&#xff0c;無需添加 sudo 1. 更新軟件 首先&#xff0c;確保系統上的所有軟件包…

UE AI 模型自動生成導入場景中

打開小馬的weix 關注下 搜索“技術鏈” 回復《《動畫》》 快速推送&#xff1b; 拿到就能用輕松解決&#xff01;幫忙點個關注吧&#xff01;

【最后203篇系列】022 用Deepseek14b提取新聞事件

這算是之前一直想做的一件事&#xff0c;趁周末趕快做了。 業務意義&#xff1a;現實中有大量的輿情&#xff0c;這對我們的決策會有比較重要的作用 技術依賴&#xff1a; 1 模型基礎能力2 消息隊列3 異步獲取消息4 時間序列庫 1 模型基礎能力 大模型發展到現在&#xff0…

電池電量檢測方法介紹,開路電壓法、庫侖積分法、內阻法

開路電壓法、庫侖積分法、內阻法、卡爾曼濾波法、混合法 開路電壓法是目前最簡單的方法&#xff0c;根據電池的特性得知&#xff0c;在電池容量與開路電壓之間存在一定的函數關系&#xff0c;當得知開路電壓時&#xff0c;可以初步估算電池的剩余電量。該方法精度不高&#xf…

微調實戰 - 使用 Unsloth 微調 QwQ 32B 4bit (單卡4090)

本文參考視頻教程&#xff1a;賦范課堂 – 只需20G顯存&#xff0c;QwQ-32B高效微調實戰&#xff01;4大微調工具精講&#xff01;知識灌注問答風格微調&#xff0c;DeepSeek R1類推理模型微調Cot數據集創建實戰打造定制大模型&#xff01; https://www.bilibili.com/video/BV1…

【Elasticsearch】基于 Word2Vec 實現文章抄襲檢測

?? 博主簡介:CSDN博客專家,歷代文學網(PC端可以訪問:https://literature.sinhy.com/#/literature?__c=1000,移動端可微信小程序搜索“歷代文學”)總架構師,15年工作經驗,精通Java編程,高并發設計,Springboot和微服務,熟悉Linux,ESXI虛擬化以及云原生Docker和K8s…