注意
使用的是pdf.js 版本为 v2.16.105。因为新版本 兼容性不太好,部分手机预览不了,所以采用v2版本。
相关依赖
"canvas":"^2.11.2","pdfjs-dist":"^2.16.105","core-js-pure":"^3.37.1","hammerjs":"^2.0.8",//这个是写手势 双指缩放的 不需要可以去掉
解决部分浏览器或者手机系统的兼容问题
//解决 structuredClone// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone#browser_compatibility// https://gitcode.com/zloirock/core-js/overview?utm_source=csdn_github_acceleratorimport structuredClone from'core-js-pure/actual/structured-clone';// 解决 TypeError: key.split(...).at is not a function// https://github.com/wojtekmaj/react-pdf/issues/1465import'core-js/features/array/at';
window.structuredClone = structuredClone;
代码
以下为在uniapp vue3 实现 h5 预览pdf文件的代码 有使用vant(手指缩放功能只写了一点,是不能用的)。
<template><div id="pdf-view" ref="pdfView"><!--<canvas v-for="page in state.pdfPages":key="page" id="pdfCanvas"/>--><div ref="pdfViewContainer"><div
v-for="pageNumber in state.pdfPages"
v-show="state.pdfPageList.includes(pageNumber)":key="pageNumber":ref="(el) => (pageRefs[pageNumber - 1] = el)"></div></div><je-loading v-show="loading"/></div></template><script setup>//解决 structuredClone// https://developer.mozilla.org/en-US/docs/Web/API/structuredClone#browser_compatibility// https://gitcode.com/zloirock/core-js/overview?utm_source=csdn_github_acceleratorimport structuredClone from'core-js-pure/actual/structured-clone';// 解决 TypeError: key.split(...).at is not a function// https://github.com/wojtekmaj/react-pdf/issues/1465import'core-js/features/array/at';import*as pdfjsWorker from'pdfjs-dist/lib/pdf.worker.js';// 解决 pdfjsWorker 未定义
window.pdfjsWorker = pdfjsWorker;
window.structuredClone = structuredClone;// if (!Array.prototype.at) {// Array.prototype.at = function (index) {// if (index < 0) {// index = this.length + index;// }// if (index >= 0 && index < this.length) {// return this[index];// }// return undefined;// };// }import Hammer from'hammerjs';import*as pdfjsWorker from'pdfjs-dist/lib/pdf.worker.js';// 解决 pdfjsWorker 未定义
window.pdfjsWorker = pdfjsWorker;import'pdfjs-dist/web/pdf_viewer.css';import*asPDFfrom'pdfjs-dist';// import * as PDF from 'pdfjs-dist/build/pdf.js';import{ useRoute }from'vue-router';import{ ref, reactive, onMounted, nextTick, defineProps }from'vue';import{ showFailToast }from'vant';const route =useRoute();const props =defineProps({src:{type: String,default:'',},});const pdfViewContainer =ref(null);const pdfView =ref(null);const pageRefs =ref([]);const loading =ref(false);const state =reactive({// 总页数pdfPages:1,pdfPageList:[],//有效页码列表// 页面缩放pdfScale:1,});let pdfDoc =null;asyncfunctionloadFile(url){// {// url,// cMapUrl: 'https://cdn.jsdelivr.net/npm/[email protected]/cmaps/',// cMapPacked: true,// }
loading.value =true;// 设置配置选项 手势缩放PDF?.DefaultViewerConfig?.set({handToolOnDblClick:true,mouseWheelScale:true,});let arrayBufferPDF;//// if (navigator.userAgent.indexOf('QQ')) {// const pdfData = await fetch(url);// arrayBufferPDF = await pdfData.arrayBuffer();// }// 解决部分机型浏览器 undefined is not an object(evaluating 'response.body.getReader')// https://www.qingcong.tech/technology/javascript/a-pdfjs-bug-in-qq.html#%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95fetch(url).then(async(pdfData)=>{
console.log('pdfData', pdfData);if(!pdfData.ok){
loading.value =false;showFailToast({message:'预览地址不存在或已失效',duration:0,});// window.JE.alert('预览地址不存在', 'error');return;}
arrayBufferPDF =await pdfData.arrayBuffer();const loadingTask = arrayBufferPDF
?PDF.getDocument({data: arrayBufferPDF }):PDF.getDocument(url);
loadingTask.promise.then((pdf)=>{
pdfDoc = pdf;// 获取pdf文件总页数
state.pdfPages = pdf.numPages;nextTick(()=>{for(let i =0; i < state.pdfPages; i++){renderPage(i +1);// 从第一页开始渲染}});});});}functioninitPinchZoom(){const pdfViewEl = pdfView.value;const hammer =newHammer(pdfViewEl);// 启用捏合缩放手势
hammer.get('pinch').set({enable:true});// 启用拖动手势,设置拖动方向为所有方向,阈值为0
hammer.get('pan').set({direction: Hammer.DIRECTION_ALL,threshold:0});let initialScale =1;// 初始缩放比例let deltaX =0;// 当前水平拖动距离let deltaY =0;// 当前垂直拖动距离let startX =0;// 拖动开始时的水平位置let startY =0;// 拖动开始时的垂直位置constMIN_SCALE=1;// 最小缩放比例constMAX_SCALE=4;// 最大缩放比例let lastPinchTime =0;// 上一次捏合事件的时间戳let lastPanTime =0;// 上一次拖动事件的时间戳// 捏合开始事件处理函数
hammer.on('pinchstart',(event)=>{
initialScale = state.pdfScale;// 记录初始缩放比例
startX = deltaX;// 记录拖动开始时的水平位置
startY = deltaY;// 记录拖动开始时的垂直位置});// 捏合移动事件处理函数
hammer.on('pinchmove',(event)=>{const currentTime = Date.now();// 节流控制,限制事件触发频率if(currentTime - lastPinchTime >50){
event.preventDefault();const scale = event.scale;// 获取当前捏合的缩放比例const newScale = Math.min(Math.max(initialScale * scale,MIN_SCALE),MAX_SCALE);// 计算新的缩放比例,限制在最小和最大缩放比例之间
state.pdfScale = newScale;// 更新缩放比例状态applyTransform();// 应用变换
lastPinchTime = currentTime;// 更新上一次捏合事件的时间戳}});// 捏合结束事件处理函数
hammer.on('pinchend',(event)=>{
initialScale = state.pdfScale;// 更新初始缩放比例为当前缩放比例limitPanPosition();// 限制拖动位置范围renderPages();// 重新渲染页面});// 拖动开始事件处理函数
hammer.on('panstart',(event)=>{
pdfViewEl.style.transition ='none';// 禁用拖动过渡效果
startX = deltaX;// 记录拖动开始时的水平位置
startY = deltaY;// 记录拖动开始时的垂直位置});// 拖动移动事件处理函数
hammer.on('panmove',(event)=>{const currentTime = Date.now();// 节流控制,限制事件触发频率if(currentTime - lastPanTime >50){const dx = event.deltaX;// 获取当前拖动的水平距离const dy = event.deltaY;// 获取当前拖动的垂直距离
deltaX = startX + dx;// 计算新的水平拖动距离
deltaY = startY + dy;// 计算新的垂直拖动距离applyTransform();// 应用变换
lastPanTime = currentTime;// 更新上一次拖动事件的时间戳}});// 拖动结束事件处理函数
hammer.on('panend',(event)=>{
pdfViewEl.style.transition ='transform 0.3s ease';// 启用拖动过渡效果limitPanPosition();// 限制拖动位置范围});// 限制拖动位置范围的函数functionlimitPanPosition(){const pdfWidth = pdfViewEl.clientWidth * state.pdfScale;// 计算PDF页面的实际宽度const containerWidth = pdfViewContainer.value.clientWidth;// 获取容器的宽度const containerHeight = pdfViewContainer.value.clientHeight;// 获取容器的高度// 计算单个页面的平均高度const averagePageHeight =
pageRefs.value.reduce((totalHeight, pageRef)=>{return totalHeight +(pageRef ? pageRef.clientHeight :0);},0)/ state.pdfPageList.length;// 估算总高度,使用PDF文档的总页数乘以单个页面的平均高度const estimatedTotalHeight = state.pdfPages * averagePageHeight * state.pdfScale;// 限制水平拖动距离,确保PDF页面在容器内部
deltaX = Math.min(0, Math.max(deltaX, containerWidth - pdfWidth));// 限制垂直拖动距离,确保PDF页面在容器内部,使用估算的总高度
deltaY = Math.min(0, Math.max(deltaY, containerHeight - estimatedTotalHeight));applyTransform();// 应用变换}// 应用变换的函数functionapplyTransform(){
pdfViewEl.style.transform =`translate(${deltaX}px, ${deltaY}px) scale(${state.pdfScale})`;// 设置PDF页面的变换样式}}functionrenderPages(){
state.pdfPageList =[];for(let i =0; i < state.pdfPages; i++){renderPage(i +1);}}functionrenderPage(num){
pdfDoc.getPage(num).then((page)=>{// 获取当前页面对应的DOM容器元素const container = pageRefs.value[num -1];// 创建一个新的canvas元素const canvas = document.createElement('canvas');// 获取canvas的2D渲染上下文const ctx = canvas.getContext('2d');// 获取设备像素比let devicePixelRatio = window.devicePixelRatio ||1;// 获取画布的backing store ratiolet backingStoreRatio =
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||1;// 获取pdfViewContainer元素的宽度const pdfWrapperElWidth =
pdfViewContainer.value.clientWidth ||
pdfViewContainer.value.offsetWidth ||
pdfViewContainer.value.style.width;// 获取PDF页面的初始视口,缩放比例为1const intialisedViewport = page.getViewport({scale:1});// 计算缩放比例,使PDF页面宽度与容器宽度一致const scale = pdfWrapperElWidth / intialisedViewport.width;// 计算设备像素比与backing store ratio的比值let ratio = devicePixelRatio / backingStoreRatio;// 根据缩放比例获取PDF页面的视口const viewport = page.getViewport({ scale });// 设置canvas的宽度为容器宽度乘以ratio,确保高分辨率下的清晰度
canvas.width = pdfWrapperElWidth * ratio;// 设置canvas的高度为视口高度乘以ratio,确保高分辨率下的清晰度
canvas.height = viewport.height * ratio;// 设置canvas的样式宽度为100%,与容器宽度一致
canvas.style.width ='100%';// 设置canvas的样式高度为auto,根据宽度自适应
canvas.style.height ='auto';// 缩放画布的渲染上下文,根据ratio进行缩放,确保在高分辨率下绘制的清晰度
ctx.scale(ratio, ratio);const renderContext ={canvasContext: ctx,
viewport,};// 设置页面容器的高度为视口高度
container.style.height =`${viewport.height}px`;
page
.render(renderContext).promise.then(()=>{
state.pdfPageList.push(num);// 如果 container 存在 canvas元素 覆盖canvas元素
container?.firstChild && container.removeChild(container.firstChild);
container && container.appendChild(canvas);}).finally(()=>{if(num === state.pdfPages){
loading.value =false;}});});}onMounted(()=>{const file = route.query.file &&JSON.parse(decodeURIComponent(route.query.file));const{ relName, previewUrl }= file ||{};if(relName){// 设置 uniapp 当前页面标题
uni.setNavigationBarTitle({title: relName,});}if(previewUrl){loadFile(previewUrl);// nextTick(() => {// initPinchZoom();// });}else{showFailToast({message:'预览地址不存在',duration:0,});}});</script><style scoped lang="less">
uni-page-body {
overflow-y: scroll;}</style>
本文转载自: https://blog.csdn.net/weixin_43245095/article/details/139701285
版权归原作者 HHH 917 所有, 如有侵权,请联系我们删除。
版权归原作者 HHH 917 所有, 如有侵权,请联系我们删除。