pdf.js实现web h5预览pdf文件(兼容低版本浏览器)

注意

使用的是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"><divv-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 * as PDF from '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;async function loadFile(url) {// {//   url,//     cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.16.105/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); // 从第一页开始渲染}});});});}function initPinchZoom() {const pdfViewEl = pdfView.value;const hammer = new Hammer(pdfViewEl);// 启用捏合缩放手势hammer.get('pinch').set({ enable: true });// 启用拖动手势,设置拖动方向为所有方向,阈值为0hammer.get('pan').set({ direction: Hammer.DIRECTION_ALL, threshold: 0 });let initialScale = 1; // 初始缩放比例let deltaX = 0; // 当前水平拖动距离let deltaY = 0; // 当前垂直拖动距离let startX = 0; // 拖动开始时的水平位置let startY = 0; // 拖动开始时的垂直位置const MIN_SCALE = 1; // 最小缩放比例const MAX_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(); // 限制拖动位置范围});// 限制拖动位置范围的函数function limitPanPosition() {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(); // 应用变换}// 应用变换的函数function applyTransform() {pdfViewEl.style.transform = `translate(${deltaX}px, ${deltaY}px) scale(${state.pdfScale})`; // 设置PDF页面的变换样式}}function renderPages() {state.pdfPageList = [];for (let i = 0; i < state.pdfPages; i++) {renderPage(i + 1);}}function renderPage(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>

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

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

相关文章

苹果新型基于home app的骚扰

近期很多用户收到了新型骚扰信息&#xff0c;通过「家庭」邀请。 故障回显&#xff1a; 你会有到一条邀请你加入 Apple 智能家庭的邀请信息&#xff0c;但邀请信息的内容是骚扰信息&#xff0c;且骚扰信息中通常携带链接&#xff0c;千万不要随意打开。 这种骚扰方式暂时没有…

通信协议—Modbus

1、modbus简介 Modbus服务器&#xff1a;接收处理来自客户端的请求&#xff0c;并返回相应的响应&#xff1b; Modbus客户端&#xff1a;向Modbus服务器发送请求&#xff0c;并接收服务器返回的响应的设备或程序&#xff1b; 2、modbus poll调试工具下载 modbus poll用于测…

Python基础教程(二十):SMTP发送邮件

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

【C语言】assert.h——断言

文章目录 主要内容调试和发布模式使用示例用法总结与注意事项 断言是一种用于在程序执行过程中进行调试的工具&#xff0c;能够帮助开发者验证程序的某些假设是否为真。如果断言失败&#xff0c;程序会终止&#xff0c;并输出一个错误消息&#xff0c;通常包含出错的文件名和行…

13【MySQL必知必会】分组数据(GROUP BY,HAVING)

SQL语句 /*第13章 分组数据关键词&#xff1a;分组数据&#xff0c;GROUP BY&#xff0c;HAVING*/-- 计算供应商ID为1003的产品数量 SELECT COUNT(*) AS num_prods FROM products WHERE vend_id 1003;-- 按供应商ID分组&#xff0c;统计每个供应商的产品数量 SELECT vend_id,…

Linux网络工具:跟踪 TCP 连接的命令tcptrack命令详解

目录 一、概述 二、 用法 1、基本用法 2、选项 三、安装 1、说明 2、Debian 系统中安装 3、centos中安装 &#xff08;1&#xff09;下载源代码 &#xff08;2&#xff09;编译源代码 &#xff08;3&#xff09;安装 四、示例和输出 1. 显示所有 TCP 连接 2. 只跟…

微信监控销售防飞单系统,让你的团队业绩稳如泰山!

团队中偶尔出现的私单、飞单问题而烦恼不已&#xff1f;你是否渴望拥有一个神器&#xff0c;能够实时监控销售过程&#xff0c;确保团队业绩的稳健增长&#xff1f;今天&#xff0c;就让我们一起探索这款神奇的“微信监控销售防飞单系统”&#xff0c;让你的销售团队如虎添翼&a…

React 渲染流程分析

React 页面是由组件组成的&#xff0c;从根组件直到叶组件&#xff0c;内部的组件数通过 Fiber 来保存并触发并发更新。页面的展示分为两部分&#xff0c;首先是初始化&#xff0c;所有组件首次展示&#xff0c;都要进行渲染&#xff0c;之后是更新流程&#xff0c;也就是页面产…

【利用python制作一个小程序生成爱心】

要利用Python制作一个小程序来生成爱心&#xff0c;我们可以使用字符图形或者利用图形库&#xff08;如turtle&#xff09;来绘制。下面我将分别展示这两种方法。 方法一&#xff1a;字符图形爱心 这种方法使用ASCII字符来模拟爱心的形状。 def print_love():heart [ ♥ …

实况:老菜鸟自力更生从零开始重学spring目标是画出一张唬人大图(二、源码下载编译)

前情提要&#xff1a;调试前的基础知识梳理 速览 “Spring”包含哪些东西源码下载源码编译1、编译工具选择&#xff1a;gradle2、使用gradle编译spring并导入idea预编译spring-oxm导入IDEA确认合适的jdk版本排除spring-aspects模块 开始调试 “Spring”包含哪些东西 可以明确的…

代码随想录算法训练营第二十四天| (回溯) 77. 组合、 216.组合总和III、17.电话号码的字母组合

77. 组合 题目链接&#xff1a;77. 组合 文档讲解&#xff1a;代码随想录 状态&#xff1a;很多细节忘了 思路&#xff1a;先画图&#xff0c;然后可以发现&#xff0c;从1到n中选择k个数&#xff0c;可以看成是一个递归过程&#xff0c;这个递归的深度就是k。然后遍历当前这层…

四舍五入 和 交换变量值 题目

题目 JAVA3 四舍五入分析&#xff1a;代码&#xff1a; JAVA4 交换变量值分析&#xff1a;代码&#xff1a;大佬代码&#xff1a; JAVA3 四舍五入 描述 定义一个int类型变量i,i为由浮点数变量d四舍五入后的整数类型&#xff0c;请将转换后的i进行输出。 输入描述&#xff1a;…

Centos7安装jdk8或11以及切换方案

目录 jdk安装 安装OpenJDK11 安装OpenJDK8 配置默认的 Java 版本 验证 全局环境变量&#xff08;选配&#xff09; 个人版&#xff08;自己可以用&#xff0c;公司不建议&#xff09; 公司版本 /etc/profile 和 ~/.bash_profile 区别 前言-与正文无关 生活远不止眼前的苦…

【动态规划】| 路径问题之不同路径 力扣62

&#x1f397;️ 主页&#xff1a;小夜时雨 &#x1f397;️ 专栏&#xff1a;动态规划 &#x1f397;️ 如何活着&#xff0c;是我找寻的方向 目录 1. 题目解析2. 代码 1. 题目解析 题目链接: https://leetcode.cn/problems/unique-paths/description/ 通常动态规划的题目有…

建筑电工精选最新模拟试题(含答案)

一、填空题 1、我国安全生产的基本方针是 安全 第一&#xff0c;预防 为主&#xff0c;综合治理。 2、特种作业人员&#xff0c;必须积极主动参加培训与考核 。既是法律法规的规定&#xff0c;也是自身工作&#xff0c;生产及生命安全 的需要 3、触电急救&#x…

如何在React组件中实现父子组件之间的通信?

在React中&#xff0c;父子组件之间的通信通常通过props&#xff08;属性&#xff09;和回调函数来实现。以下是具体的实现方式&#xff1a; 父组件向子组件传递数据&#xff08;通过props&#xff09; 在父组件中&#xff0c;你可以通过向子组件传递props来发送数据。子组件…

【Tkinter界面】Canvas 图形绘制(03/5)

文章目录 一、说明二、画布和画布对象2.1 画布坐标系2.2 鼠标点中画布位置2.3 画布对象显示的顺序2.4 指定画布对象 三、你应该知道的画布对象操作3.1 什么是Tag3.2 操作Tag的函数 https://www.cnblogs.com/rainbow-tan/p/14852553.html 一、说明 Canvas&#xff08;画布&…

双指针练习:和为s的两个数字

题目链接&#xff1a;剑指offer 57.和为s的两个数字 题目描述&#xff1a; 购物车内的商品价格按照升序记录于数组 price。请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况&#xff0c;返回任一结果即可。 示例 1&#xff1a; 输入&#xff1a;price [3, 9…

【Windows】配置Flutter开发环境

一、下载 flutter sdk 点此跳至下载官网 下载好flutter sdk&#xff0c;并解压到自定义的位置。 二、配置环境变量 此电脑 --> 右键 选择 属性 --> 点击 高级系统设置 --> 会弹出系统属性的窗口&#xff0c;点击 环境变量 按钮 1.配置加速镜像地址 PUB_HOSTED_…

Web前端设计毕业论文:深度探索与未来展望

Web前端设计毕业论文&#xff1a;深度探索与未来展望 在数字化时代&#xff0c;Web前端设计作为互联网应用的重要组成部分&#xff0c;其重要性和复杂性日益凸显。本论文旨在深度探索Web前端设计的关键要素、发展趋势以及面临的挑战&#xff0c;为未来的研究和实践提供有价值的…