当前位置: 首页 > news >正文

vue-pdf 实现pdf预览、高亮、分页、定位功能

vue-pdf 实现pdf预览、高亮、分页、定位功能(基于vue2.0!!!)

  • 前言
  • 一、实现步骤
    • 1.引入库
    • 2.示例代码
    • 3.触发高亮事件
    • 4.分页高亮
    • 5.跳转指定页面并高亮(不分页)
  • 参考
  • 笔记(重要)
  • 总结


前言

vue-pdf 实现pdf预览、高亮、分页、定位功能(基于vue2.0!!!
找了一圈 vue-pdf 高亮,举步维艰,只能自己实现了
效果图:
在这里插入图片描述


一、实现步骤

1.引入库

npm install --save vue-pdf

2.示例代码

参考:vue 使用 vue-pdf 实现文件在线预览.md

3.触发高亮事件

<template><el-container><el-main><el-row type='flex'><el-col :span='14'><!-- 文件列表表格 --><el-table :data='dataList'v-loading='loading'ref='table'borderrow-key='id'@row-dblclick='editChunk'@row-click='rowClick'@selection-change='tableSelectChange'><el-table-columntype='selection'reserve-selectionwidth='55'></el-table-column><el-table-column prop='name' label=''></el-table-column></el-table></el-col><el-col :span='10' ><!--            <pdf-viewer v-if='isPdf' ref='pdf' url='http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf'></pdf-viewer>--><!-- 带分页的高亮 --><pdf-page-viewer v-if='isPdf' ref='pdf' url='http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf'></pdf-page-viewer></el-col></el-row></el-main></el-container>
</template>
<script>
import PdfViewer from './PdfViewer.vue'
import ParseChunkDialog from './ParseChunkDialog.vue'
import VuePdfViewer from './VuePdfViewer.vue'
import PdfPageViewer from './PdfPageViewer.vue'export default {name: 'Chunk',components: { PdfPageViewer, VuePdfViewer, ParseChunkDialog, PdfViewer },props: {docId: {type: String,default: ''}},data () {return {dataList: [{'id': '1','name': 'test',// 后端给出位置,参数分别是  page, left, right, top, bottom'positions': [[2,79,518,106,193],[2,82,520,296,314],[2,330,768,296,314]]}],selectedIds: [], // 已选择列表doc: {},loading: false,loaded: false,totalCount: 0,queryParams: {doc_id: '',keywords: '',size: 10,page: 1}}},created () {},computed: {isPdf () {return true}},methods: {goBack () {this.$emit('goBack')},// 表格多选tableSelectChange (val) {this.selectedIds = val.map(item => item.id)},editChunk (data) {},rowClick (data) {this.$refs.pdf && this.$refs.pdf.highlight(data.positions)},// 获取id集getIds (id) {return id ? [id] : this.selectedIds}}
}
</script>

4.分页高亮

  • PdfPageViewer.vue
<template><div v-loading='loading':element-loading-text="'拼命加载中'+percentage"element-loading-spinner='el-icon-loading'><pdf ref='pdf':src='url':page='pageNum':rotate='pageRotate'@progress='loadedRatio = $event'@page-loaded='pageLoaded($event)'@loaded='loaded'@num-pages='pageTotalNum=$event'@error='pdfError($event)'id='pdfID'></pdf><div class='tools' v-show='pageTotalNum'><el-button type='primary' @click='prePage'><i class='el-icon-arrow-left'></i>上一页</el-button>{{ pageNum }}/{{ pageTotalNum }}<el-button type='primary' @click='nextPage'>下一页<i class='el-icon-arrow-right'></i></el-button></div></div>
</template><script>
import pdf from 'vue-pdf'
export default {name: 'PdfPageViewer',components: {pdf},props: {url: {type: String,default: ''}},data () {return {loading: true,pageNum: 1,pageTotalNum: 1,pageRotate: 0,loadedRatio: 0,pdfWidth: 595,zoom: 1,tid: null,positions: [],highlightDivs: [] // 用于存储高亮显示的 div 元素}},computed: {percentage () {return parseFloat((this.loadedRatio * 100).toFixed(2)) + '%'}},mounted () {console.log('PDF mounted url:', this.url)window.addEventListener('resize', this.updateHighlights)},methods: {loaded () {this.loading = falseconsole.log('loaded', this.$refs.pdf.pdf)this.$refs.pdf.pdf.getPage().then((pdf) => {console.log('aaaaaaaaaaa', pdf.view[2])this.pdfWidth = pdf.view[2]console.log('pdfWidth', this.pdfWidth)this.updateHighlights()})},pageLoaded (page) {this.loading = falseconsole.log('page loaded', page)},pdfError (error) {console.error(error)},prePage () {this.clearHighlights()var page = this.pageNumpage = page > 1 ? page - 1 : this.pageTotalNumthis.pageNum = page},nextPage () {this.clearHighlights()var page = this.pageNumpage = page < this.pageTotalNum ? page + 1 : 1this.pageNum = page},clearHighlights () {this.highlightDivs.forEach(div => {if (div.parentNode) {div.parentNode.removeChild(div)}})this.highlightDivs = []},highlight (positions) {if (!this.positions) returnconsole.log('positions', positions)this.clearHighlights()// 为每个高亮区域创建新的 div 元素positions.forEach(highlight => {const [page, left, right, top, bottom] = highlightthis.pageNum = page || 1const highlightDiv = document.createElement('div')const highlightSize = {}highlightSize.top = top * this.zoomhighlightSize.left = left * this.zoomhighlightSize.height = (bottom - top) * this.zoomhighlightSize.width = (right - left) * this.zoomconsole.log(highlightSize)highlightDiv.setAttribute('style', `top:${highlightSize.top}px;left:${highlightSize.left}px;height:${highlightSize.height}px;width:${highlightSize.width}px;`)highlightDiv.className = 'highlight__part'// 将新的高亮显示元素添加到 PDF 容器中document.getElementById('pdfID').appendChild(highlightDiv)// 将新的 div 元素添加到 highlightDivs 数组中以便管理this.highlightDivs.push(highlightDiv)this.positions = positions})},updateHighlights () {let clientWidth = this.$refs.pdf.$el.clientWidththis.zoom = clientWidth / this.pdfWidthconsole.log(this.zoom, clientWidth, this.pdfWidth)clearTimeout(this.tid)this.tid = setTimeout(() => {this.highlight(this.positions)}, 300)}},// 在 beforeDestroy 钩子中移除事件监听器beforeDestroy () {window.removeEventListener('resize', this.updateHighlights)}
}
</script><style>
#pdfID {overflow-x: hidden;overflow-y: hidden;
}
.tools {text-align: center;
}.highlight__part {position: absolute;background: #ffe28f;opacity: 0.5;transition: background .3s;
}
</style>

5.跳转指定页面并高亮(不分页)

  • PdfViewer.vue
<template><div ref='pdfDiv' id='pdfDiv'><vue-pdf-viewer  v-for="page in numPages" @loaded='loaded' :ref='"pdf"+page' :page="page"  :url="url" ></vue-pdf-viewer></div>
</template><script>
import pdf from 'vue-pdf'
import VuePdfViewer from './VuePdfViewer.vue'export default {components: {VuePdfViewer,pdf},props: {url: {type: String,default: ''}},data () {return {numPages: 1,loadedPage: 0,pdfWidth: 595,pdfHeight: 800,clientWidth: 595,clientHeight: 800,tid: null,widthZoom: 1,heightZoom: 1,positions: [],highlightDivs: [] // 用于存储高亮显示的 div 元素}},mounted () {console.log('PDF loaded', this.url)this.getNumPages()console.log(this.$refs.pdfDiv)window.addEventListener('resize', this.updateHighlights)},methods: {getNumPages () {var loadingTask = pdf.createLoadingTask(this.url)console.log(loadingTask, 'pdf 获取总页数成功')loadingTask.promise.then(pdf => {this.numPages = pdf.numPages}).catch(() => {console.error('pdf 获取总页数失败,重新获取...')setTimeout(() => {this.getNumPages()}, 300)})},scrollTo (page) {this.$nextTick(() => {let pdfDiv = this.$refs.pdfDivpdfDiv.scrollTop = this.clientHeight * (page - 1) * this.heightZoom})},loaded (width, pdfHeight, page) {console.log('pdf 第 ' + page + ' 加载完成')this.$nextTick(() => {this.loadedPage = pagethis.pdfWidth = widththis.pdfHeight = pdfHeightsetTimeout(() => {this.updateHighlights()}, 1500)})},highlight (positions) {if (!this.positions) returnthis.clearHighlights()// 为每个高亮区域创建新的 div 元素positions.forEach(highlight => {const [page, left, right, top, bottom] = highlightif (!page) returnthis.scrollTo(page)const highlightDiv = document.createElement('div')const highlightSize = {}highlightSize.top = top * this.widthZoomhighlightSize.left = left * this.widthZoomhighlightSize.height = (bottom - top) * this.widthZoomhighlightSize.width = (right - left) * this.widthZoomhighlightDiv.setAttribute('style', `top:${highlightSize.top}px;left:${highlightSize.left}px;height:${highlightSize.height}px;width:${highlightSize.width}px;`)highlightDiv.className = 'highlight__part'// 将新的高亮显示元素添加到 PDF 容器中document.getElementById('pdf' + page).appendChild(highlightDiv)// 将新的 div 元素添加到 highlightDivs 数组中以便管理this.highlightDivs.push(highlightDiv)this.positions = positions})},/*** 分辨率发生变化重新计算缩放并重新绘制高亮区域*/updateHighlights () {clearTimeout(this.tid)// 防抖this.tid = setTimeout(() => {this.clientHeight = document.getElementById('pdf' + (this.loadedPage || 1))?.clientHeightthis.heightZoom = this.clientHeight / this.pdfHeightthis.clientWidth = this.$refs.pdfDiv.clientWidththis.widthZoom = this.clientWidth / this.pdfWidththis.highlight(this.positions)}, 300)},clearHighlights () {this.highlightDivs.forEach(div => {if (div.parentNode) {div.parentNode.removeChild(div)}})this.highlightDivs = []}},// 在 beforeDestroy 钩子中移除事件监听器beforeDestroy () {window.removeEventListener('resize', this.updateHighlights)}
}
</script><style>
#pdfDiv{overflow-y: auto;height: calc(100vh - 420px);
}
</style>

子组件(不分页需用到)

  • VuePdfViewer.vue
<template><div v-loading='loading':element-loading-text="'拼命加载中'+percentage"element-loading-spinner='el-icon-loading'><pdf :ref='pdfRef'class='overflow-none':key="page"  :src="url" :page="page"@progress='loadedRatio = $event'@loaded='loaded':id='pdfId'></pdf></div>
</template><script>
import pdf from 'vue-pdf'export default {components: {pdf},props: {url: {type: String,default: ''},page: {type: Number,default: 1}},data () {return {loading: true,tid: null,loadedRatio: 0}},computed: {pdfRef () {return 'ref' + this.page},pdfId () {return 'pdf' + this.page},percentage () {return parseFloat((this.loadedRatio * 100).toFixed(2)) + '%'}},mounted () {this.reloadCheck()},methods: {loaded () {this.loading = falsethis.$refs[this.pdfRef].pdf.getPage().then((pdf) => {let pdfWidth = pdf.view[2]let pdfHeight = pdf.view[3]this.$emit('loaded', pdfWidth, pdfHeight, this.page)})},reloadCheck () {if (!this.loading && this.loadedRatio === 1) {console.log('pdf 加载失败,重新加载...')this.$refs[this.pdfRef].pdf.loadDocument(this.url)setTimeout(() => {this.reloadCheck()}, 3000)}}}
}
</script><style scoped>.overflow-none {overflow-x: hidden;overflow-y: hidden;
}
</style>

参考

vue 使用 vue-pdf 实现文件在线预览.md

笔记(重要)

如果需要实现高亮需要增强 \node_modules\vue-pdf\src\pdfjsWrapper.js 在288行后面增加:

		this.getPage = function() {return pdfDoc.getPage(1)}

提示无法加载组件或者页面出不来,修改 \node_modules\vue-pdf\src\vuePdfNoSss.vue 替换

<style src="./annotationLayer.css"></style>
<script>import componentFactory from './componentFactory.js'import PdfjsWorker from 'pdfjs-dist/es5/build/pdf.worker.js'if ( process.env.VUE_ENV !== 'server' ) {var pdfjsWrapper = require('./pdfjsWrapper.js').default;var PDFJS = require('pdfjs-dist/es5/build/pdf.js');if ( typeof window !== 'undefined' && 'Worker' in window && navigator.appVersion.indexOf('MSIE 10') === -1 ) {// var PdfjsWorker = require('worker-loader!pdfjs-dist/es5/build/pdf.worker.js');PDFJS.GlobalWorkerOptions.workerPort = new PdfjsWorker();
}var component = componentFactory(pdfjsWrapper(PDFJS));
} else {var component = componentFactory({});
}export default component;
</script>

并在 vue.config.js 中的 chainWebpack: config => { 加入以下代码

    config.module.rule('worker').test(/\.worker\.js$/).use('worker-loader').loader('worker-loader').options({inline: true,fallback: false}).end()

总结

只能说vue版本太老了…

相关文章:

  • 问题记录:end value has mixed support, consider using flex-end instead
  • docker和ufw冲突问题
  • SpringBoot开发——整合P6Spy详细记录SQL执行耗时情况
  • 智能识别猫猫
  • Day 1 词汇备战
  • 使用MyBatis-Plus与Thymeleaf在Spring Boot中实现增删改查
  • 【CSS】鼠标 、轮廓线 、 滤镜 、 堆叠层级
  • 学习一下怎么用git
  • 阿里云AlibabaCloudLinux php 安装 mysqli 扩展
  • 位运算--(二进制中1的个数)
  • ESP32-定时器中断
  • uniapp vue3 使用echarts绘制图表 柱状图等
  • 缓存穿透 问题(缓存空对象)
  • Java | Leetcode Java题解之第436题寻找右区间
  • Python 如何使用 unittest 模块编写单元测试
  • hexo+github搭建个人博客
  • “大数据应用场景”之隔壁老王(连载四)
  • 【391天】每日项目总结系列128(2018.03.03)
  • 【MySQL经典案例分析】 Waiting for table metadata lock
  • Javascript 原型链
  • Javascript基础之Array数组API
  • js 实现textarea输入字数提示
  • JWT究竟是什么呢?
  • Laravel Telescope:优雅的应用调试工具
  • Making An Indicator With Pure CSS
  • nodejs:开发并发布一个nodejs包
  • orm2 中文文档 3.1 模型属性
  • scala基础语法(二)
  • Spring框架之我见(三)——IOC、AOP
  • vuex 笔记整理
  • Wamp集成环境 添加PHP的新版本
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 理解 C# 泛型接口中的协变与逆变(抗变)
  • 前端学习笔记之观察者模式
  • 思考 CSS 架构
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • 用Python写一份独特的元宵节祝福
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • #Linux(Source Insight安装及工程建立)
  • #考研#计算机文化知识1(局域网及网络互联)
  • #在 README.md 中生成项目目录结构
  • (1)(1.13) SiK无线电高级配置(五)
  • (14)学习笔记:动手深度学习(Pytorch神经网络基础)
  • (Redis使用系列) SpirngBoot中关于Redis的值的各种方式的存储与取出 三
  • (附源码)计算机毕业设计大学生兼职系统
  • (利用IDEA+Maven)定制属于自己的jar包
  • (全注解开发)学习Spring-MVC的第三天
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (十三)Maven插件解析运行机制
  • (译) 函数式 JS #1:简介
  • .axf 转化 .bin文件 的方法
  • .gitignore文件使用
  • .NET Core 和 .NET Framework 中的 MEF2
  • .Net Core缓存组件(MemoryCache)源码解析
  • .NET 使用配置文件