【PDF.js】2023 最新 PDF.js 在 Vue3 中的使用

因为自己写业务要定制各种 pdf 预览情况(可能),所以采用了 pdf.js 而不是各种第三方封装库,主要还是为了更好的自由度。

一、PDF.js 介绍

官方地址
中文文档

PDF.js 是一个使用 HTML5 构建的便携式文档格式查看器。
pdf.js 是社区驱动的,并由 Mozilla 支持。我们的目标是为解析和呈现 PDF 创建一个通用的、基于 Web 标准的平台。

二、 安装方法

1、下载 pdf.js

下载地址
在这里插入图片描述
我下载的版本是 pdfjs-4.0.189-dist
在这里插入图片描述

2、解压包并放到项目中

解压后将完整文件夹放到 vue3 的 public 文件夹内
在这里插入图片描述

3、屏蔽跨域错误,允许跨域

web/viewer.mjs 内找到搜索 throw new Error("file origin does not match viewer's") 并注释掉,如果不注释,可能会出现跨域错误,无法正常预览文件
在这里插入图片描述
这样就算安装完成了,后面我们开始在项目中使用。

三、基础使用

1、创建 PDF 组件

我们可以创建一个 PDF 组件,代码如下:

<script setup lang="ts">
import { onMounted, ref } from 'vue';
interface Props {url: string; // pdf文件地址
}
const props = defineProps<Props>();
const pdfUrl = ref(''); // pdf文件地址
const fileUrl = '/pdfjs-4.0.189-dist/web/viewer.html?file='; // pdfjs文件地址onMounted(() => {// encodeURIComponent() 函数可把字符串作为 URI 组件进行编码。// 核心就是将 iframe 的 src 属性设置为 pdfjs 的地址,然后将 pdf 文件的地址作为参数传递给 pdfjs// 例如:http://localhost:8080/pdfjs-4.0.189-dist/web/viewer.html?file=http%3A%2F%2Flocalhost%3A8080%2Fpdf%2Ftest.pdfpdfUrl.value = fileUrl + encodeURIComponent(props.url);
});
</script><template><div class="container"><iframe :src="pdfUrl" width="100%" height="100%"></iframe></div>
</template><style scoped lang="scss">
.container {width: 100%;height: 100%;
}
</style>

2、使用组件

比如我们需要预览 public 下的一个 test.pdf 文件

<div class="pdf-box"><PDF url="/public/test.pdf" />
</div>

下面是界面默认预览效果
在这里插入图片描述

四、进阶使用

1、页面跳转(传参)

比如我们要跳到第 10 页,我们可以在地址里面添加参数 &page=${10}

pdfUrl.value = fileUrl + encodeURIComponent(props.url) + `&page=${10}`;

viewer.mjs 找到 setInitialView 函数,注意是下面这个:
在这里插入图片描述
重点:在函数末尾最下面添加下面的跳转代码(写在上面会报错,因为还没有获取到实例)

    console.log(this.pdfViewer);// 获取url参数function getQueryVariable(variable) {var query = window.location.search.substring(1);var vars = query.split('&');for (var i = 0; i < vars.length; i++) {var pair = vars[i].split('=');if (pair[0] == variable) {return pair[1];}}return false;}// 跳转到指定页const page = getQueryVariable('page');console.log(page);if (page) {this.pdfViewer.currentPageNumber = Number(page);}

2、文本标注(传参)

某些时候我们需要跳转到指定页面,然后自动标注文本,这个时候就需要自动标注了
在这里插入图片描述
代码跟跳转一样,写在后面就可以了

    // 自动高亮文本(要解码)decodeURIComponent: 解码const markText = decodeURIComponent(getQueryVariable('markText'));console.log('markText===>', markText);if (markText) {// 对查询输入框进行赋值document.getElementById('findInput').value = markText;// 点击高亮按钮实现高亮显示关键词document.getElementById('findHighlightAll').click();}

目前我还没有找到批量标注的办法,批量标注建议还是使用下面页面+坐标,遮罩的方法
在这里插入图片描述

3、添加遮罩高亮(页码+坐标)

主要是为了解决批量标注的问题,因为 pdfjs 原生只支持单文本,不支持批量,要修改大量源码(我能力不行,太难了😥)

所以还是换了种方案,就是后端返回页码+坐标,添加遮罩层渲染的方式。

这种方法主要是找到渲染的 dom元素,因为渲染的pdf有一个叫做 data-page-number="1" 的属性,因此我们可以通过 js 的 querySelectorAll 选择器找到对应属性的 dom 元素,然后再操作添加遮罩就可以了,代码放在下面。
在这里插入图片描述

    // 测试的坐标const content_pos_1 = {x: 0.5135954145019941,y: 0.4662730487881233,};const content_pos_2 = {x: 0.7135954145019941,y: 0.8662730487881233,};// 查找属性 data-page-number='页码' 的 dom 元素const pageList = document.querySelectorAll(`[data-page-number='${page}']`);console.log('查询到的dom列表===>\n', pageList[1]);// 查询到的第一个是左侧小菜单页码div,第二个是才是展示的divconst pageView = pageList[1];console.log('右侧展示的dom===>\n', pageView);// 在元素上画一个divconst div = document.createElement('div');div.style.width = (content_pos_2.x - content_pos_1.x) * 100 + '%';div.style.height = (content_pos_2.y - content_pos_1.y) * 100 + '%';div.style.backgroundColor = 'rgb(255, 255, 0, 0.1)';div.style.position = 'absolute';div.style.top = content_pos_1.y * 100 + '%';div.style.left = content_pos_1.x * 100 + '%';div.style.zIndex = '1'; // pdfjs 文本的层级是2 所以这里要设置为1 放着不能复制pageView.appendChild(div);

渲染到pdf上就是下面的样子:
在这里插入图片描述

4、添加遮罩高亮(缩放动态更新)

因为 pdf 会缩放的缘故,缩放的话会重新更新 pdf ,我们添加的 div 就会消失,所以我们要在重新更新的时候重新添加,源码内部重新添加的函数在这个位置: #updateUIState
在这里插入图片描述
我们只需要将修改后重新添加的代码放在尾部就行
首先我们要修改第三部分的代码

   // 测试的坐标const content_pos_1 = {x: 0.5135954145019941,y: 0.4662730487881233,};const content_pos_2 = {x: 0.7135954145019941,y: 0.8662730487881233,};// pdf 缩放会重新设置,所以放在window保存,其他地方要用window.page = page;window.shade = {width: (content_pos_2.x - content_pos_1.x) * 100 + '%',height: (content_pos_2.y - content_pos_1.y) * 100 + '%',top: content_pos_1.y * 100 + '%',left: content_pos_1.x * 100 + '%',};console.log(window.shade);// 查找属性 data-page-number='页码' 的 dom 元素const pageList = document.querySelectorAll(`[data-page-number='${page}']`);console.log('查询到的dom列表===>\n', pageList[1]);// 查询到的第一个是左侧小菜单页码div,第二个是才是展示的divconst pageView = pageList[1];console.log('右侧展示的dom===>\n', pageView);// 在元素上画一个divconst div = document.createElement('div');div.id = 'shade';div.style.width = window.shade.width;div.style.height = window.shade.height;div.style.backgroundColor = 'rgb(255, 255, 0, 0.1)';div.style.position = 'absolute';div.style.top = window.shade.top;div.style.left = window.shade.left;div.style.zIndex = '1';pageView.appendChild(div);

然后在 #updateUIState 函数的末尾添加下面的新增代码

    setTimeout(() => {if (!window.page) return;const pageList = document.querySelectorAll(`[data-page-number='${window.page}']`);const pageView = pageList[1];// 删除 id 为 shade 的元素(旧遮罩)const shade = document.getElementById('shade');if (shade) {shade.remove();}const div = document.createElement('div');div.id = 'shade';div.style.width = window.shade.width;div.style.height = window.shade.height;div.style.backgroundColor = 'rgb(255, 255, 0, 0.1)';div.style.position = 'absolute';div.style.top = window.shade.top;div.style.left = window.shade.left;div.style.zIndex = '1';pageView.appendChild(div);}, 500);

最终效果如下:
在这里插入图片描述
ps:如果要做大量的页面+坐标渲染(后端返回的是个数组),修改下上面的代码逻辑就行,传参自己写,不难的

当然,也可以看下面的代码哈哈哈,我还是写出来吧

5、添加遮罩高亮(数组批量跨页渲染)

假设后端返回的数据格式是这样的,是一个包含 页码、坐标的标注数组,我们需要在每个页码内渲染遮罩
在这里插入图片描述

我们就需要这样传参
在这里插入图片描述
setInitialView(storedHash, { rotation, sidebarView, scrollMode, spreadMode } = {}) 初始化函数中:

    window.content_pos = JSON.parse(decodeURIComponent(getQueryVariable('content_pos')));console.log(window.content_pos[0]);window.content_pos.forEach((item, index) => {const page = item.page_no;const shade = {width: (item.right_bottom.x - item.left_top.x) * 100 + '%',height: (item.right_bottom.y - item.left_top.y) * 100 + '%',top: item.left_top.y * 100 + '%',left: item.left_top.x * 100 + '%',};console.log(shade);const pageList = document.querySelectorAll(`[data-page-number='${page}']`);const pageView = pageList[1];const div = document.createElement('div');div.id = 'shade' + index;div.style.width = shade.width;div.style.height = shade.height;div.style.backgroundColor = 'rgb(255, 255, 0, 0.1)';div.style.position = 'absolute';div.style.top = shade.top;div.style.left = shade.left;div.style.zIndex = '1';pageView.appendChild(div);});

#updateUIState(resetNumPages = false) 更新函数中:

    setTimeout(() => {if (window.content_pos) {window.content_pos.forEach((item, index) => {const shadeEl = document.getElementById('shade' + index);if (shadeEl) {shadeEl.remove();}const page = item.page_no;const shade = {width: (item.right_bottom.x - item.left_top.x) * 100 + '%',height: (item.right_bottom.y - item.left_top.y) * 100 + '%',top: item.left_top.y * 100 + '%',left: item.left_top.x * 100 + '%',};const pageList = document.querySelectorAll(`[data-page-number='${page}']`);const pageView = pageList[1];const div = document.createElement('div');div.id = 'shade' + index;div.style.width = shade.width;div.style.height = shade.height;div.style.backgroundColor = 'rgb(255, 255, 0, 0.1)';div.style.position = 'absolute';div.style.top = shade.top;div.style.left = shade.left;div.style.zIndex = '1';pageView.appendChild(div);});}}, 500);

效果展示,可以实现跨页,多页渲染
在这里插入图片描述

后续根据开发业务持续更新😁

感谢大佬们的无私分享

详细|vue中使用PDF.js预览文件实践
vue3项目使用pdf.js插件实现:搜索高亮、修改pdf.js显示的页码、向pdf.js传值、控制搜索、处理接口文件流
pdf.js根据路径里传参数高亮显示关键字(跳转到对应页面)

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

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

相关文章

flutter与原生Android通信方式之MethodChannel

闲来无事&#xff0c;flutter好久没看了&#xff0c;上次折腾flutter与Android通信没折腾完&#xff0c;有些事情耽搁了&#xff0c;这次继续 演示效果&#xff1a; flutter与Android原生通信 flutter端 import package:flutter/cupertino.dart; import package:flutter/mater…

ThreeJs实现简单的动画

上一节实现可用鼠标控制相机的方式实现动态效果&#xff0c;但很多时候是需要场景自己产恒动态效果&#xff0c;而不是通过鼠标拖动&#xff0c;此时引入一个requestAnimationFrame方法&#xff0c;它实际上是通过定时任务的方式&#xff0c;每隔一点时间改变场景中内容后重新渲…

笔记:如何搭建一套前端监控系统?(持续更新中)

数据敏感处理 数据加密&#xff0c;对涉及用户隐私的数据做到加密防护 独立部署&#xff0c;不和其它应用共享监控系统 不采集具体数据&#xff0c;只采集用户操作数据 错误采集 Runtime Error: JS运行错误&#xff0c;可通过error监听器捕获 load Error: 资源加载错误&#x…

Ant Design Vue 树形表格计算盈收金额

树形表格计算 一、盈收金额计算1、根据需要输入的子级位置&#xff0c;修改数据2、获取兄弟节点数据&#xff0c;并计算兄弟节点的金额合计3、金额合计&#xff0c;遍历给所有的父级 一、盈收金额计算 1、根据需要输入的子级位置&#xff0c;修改数据 2、获取兄弟节点数据&am…

销售管理系统的实用性怎么样?

销售管理系统好用吗&#xff1f;好用&#xff0c;销售管理系统可以管理销售流程、自动化大量重复性工作&#xff0c;让销售人员从琐碎的任务中挣脱出来&#xff0c;投入到客户跟进和维护客户关系之中。那么&#xff0c;CRM系统的好用体现在哪些方面&#xff1f; 1.加速销售流程…

react中的state

没想到hooks中也有state这一说法 看下面的两个案例 1、无state变化不会执行父子函数 2、有state更改执行父子函数

CDN加速在网站搭建中的必要性与优势分析

随着互联网的快速发展&#xff0c;网站已经成为企业展示和用户交互的主要平台。在构建一个高性能、用户体验良好的网站时&#xff0c;CDN&#xff08;内容分发网络&#xff09;的应用变得愈发重要。本文将从网站搭建的角度出发&#xff0c;深入分析CDN加速的必要性以及在提升网…

深度学习之六(自编码器--Autoencoder)

概念 自编码器(Autoencoder)是一种神经网络架构,用于无监督学习和数据的降维表示。它由两部分组成:编码器(Encoder)和解码器(Decoder)。 结构: 编码器(Encoder): 接收输入数据并将其压缩为潜在表示(latent representation),通常比输入数据的维度要低。编码器的…

最详细的软件测试面试题整理与分析

前言 时光荏苒&#xff0c;一转眼到了2023年末尾&#xff0c;2024年也快要来了&#xff0c;人员就业市场以往的寒冬也貌似有了转暖的迹象&#xff0c;身边大批的就业人员也开始了紧张的备战之中。 近几周也和多家合作公司的HR进行了沟通&#xff0c;发现虽然岗位就业情况较去年…

vue3中引入svg矢量图

vue3中引入svg矢量图 1、前言2、安装SVG依赖插件3、在vite.config.ts 中配置插件4、main.ts入口文件导入5、使用svg5.1 在src/assets/icons文件夹下引入svg矢量图5.2 在src/components目录下创建一个SvgIcon组件5.3 封装成全局组件&#xff0c;在src文件夹下创建plugin/index.t…

SQLserver 数据库导入MySQL的方法

原文&#xff1a; https://blog.csdn.net/lht631935612/article/details/132086172#httpspanbaiducoms1TlLiRI9stxqTcwBJ5p6UAE993BEE68EA5EFBC9Ahttpspanbaiducoms1TlLiRI9stxqTcwBJ5p6UA2020E68F90E58F96E7A081EFBC9Av6d5_font_colordd0000v6d5font_8 下载链接&#xff1a;…

使用openfeign调用下载流的文件不完整的替代方案

OpenFeign是一种声明式的Web服务客户端&#xff0c;它使得编写HTTP客户端变得更加简单和直观。它使用了注解方式来描述HTTP API&#xff0c;使得开发者可以使用Java接口来调用远程HTTP服务。 OpenFeign的核心特点包括&#xff1a; 声明式API: 您可以使用注解声明要调用的远程AP…

一键创新 | 拓世法宝AI智能直播一体机激发房产自媒体创造力

在数字化时代&#xff0c;房产销售已然不再是传统的模式。随着社交媒体和自媒体的兴起&#xff0c;短视频直播成为房产自媒体营销的新风口。然而&#xff0c;行业也面临着诸多挑战&#xff0c;如何更好地利用新媒体拓展市场&#xff0c;提升自媒体效果成为摆在业内人士面前的难…

JMeter测试报错422 Unprocessable Entity

添加HTTP信息头&#xff1a; ​ HTTP请求-》添加-〉配置元件-》HTTP信息头管理器 ​ 如果需要送json&#xff0c;需要添加Content-Type:application/json&#xff0c;否则会报【422 Unprocessable Entity】

好用的CRM系统到底有多重要?怎么选?

我们都知道&#xff0c;CRM软件可以让企业效率加倍。但如果选错了CRM&#xff0c;企业损失点钱是小&#xff0c;客户转化率下降才是大。下面我们就来说说&#xff0c;市面上有哪些好用的CRM&#xff1f;以及好用的CRM软件的重要性。 好用的CRM软件的重要性&#xff1a; 客户管…

Qt 软件调试(一) Log日志调试

终于这段时间闲下来了&#xff0c;可以系统的编写Qt软件调试的整个系列。前面零零星星的也有部分输出&#xff0c;但终究没有形成体系。借此机会&#xff0c;做一下系统的总结。慎独、精进~ 日志是有效帮助我们快速定位&#xff0c;找到程序异常点的实用方法。但是好的日志才能…

百度 文心一言 sdk 试用

JMaven Central: com.baidu.aip:java-sdk (sonatype.com) Java sdk地址如上&#xff1a; 文心一言开发者 文心一言 (baidu.com) ERNIE Bot SDK https://yiyan.baidu.com/developer/doc#Fllzznonw ERNIE Bot SDK提供便捷易用的接口&#xff0c;可以调用文心一言的能力&#…

口袋参谋:如何避免宝贝被降权?这招屡试屡爽!

​至少99.99999%的店铺在今年都被降权过&#xff01;各家店铺被降权的原因&#xff0c;无非就一个原因&#xff0c;那就是s单&#xff01; s单的风险也就两种&#xff0c;一是操作问题&#xff0c;二是账号问题。 操作问题被降权&#xff0c;这个大家都心知肚明&#xff0c;s…

5大原因,设备校准为什么是实验室搬迁后的首要任务?

实验室搬迁是一个复杂而紧张的过程。要考虑的事情太多&#xff0c;很容易忽视您最重要的任务之一——检查设备在新环境中的性能。 校准对于确保设备安全运行和遵守监管标准至关重要。 1.保持合规性并遵守法律要求 生物技术和制药等行业有特定的校准要求&#xff0c;实验室必…

Java详解之I/O[BIO、NIO、AIO使用方法和示范代码]

前言&#xff1a; 小弟能力不足&#xff0c;认知有限&#xff0c;难免考虑不全面&#xff0c;希望大佬能给出更好的建议&#xff0c;指出存在的问题和不足&#xff0c;在此跪谢。 IO发展史 Java中对于I/O能力的支持主要分为三个比较关键的阶段&#xff1a; BIO 第一个阶段…