|
@@ -0,0 +1,326 @@
|
|
|
+<template>
|
|
|
+ <div class="pdf">
|
|
|
+ <div class="btnbox">
|
|
|
+ <el-button type="primary" class="el-icon-tickets" size="mini" @click="outlineShow = !outlineShow"></el-button>
|
|
|
+ <!-- <el-button type="primary" v-if="mode == 'pc'" class="el-icon-arrow-left left" size="mini" @click="pre_page"></el-button>
|
|
|
+ <el-button type="primary" v-if="mode == 'pc'" class="el-icon-arrow-right right" size="mini" @click="next_page"></el-button> -->
|
|
|
+ <el-button type="primary" v-if="mode == 'pc'" class="el-icon-plus" size="mini" @click="zoom_big"></el-button>
|
|
|
+ <el-button type="primary" v-if="mode == 'pc'" class="el-icon-minus" size="mini" @click="zoom_small"></el-button>
|
|
|
+ </div>
|
|
|
+ <outline v-if="outlineShow" @outline_item="outline_item" :outlineData="outlineData" class="outline" :style="{ width: outlineWidth }"></outline>
|
|
|
+ <div class="canvasBox">
|
|
|
+ <!-- <canvas v-for="page in pdf_pages" :id="'the-canvas'+page" :key="page" class="canvas"></canvas> -->
|
|
|
+ <canvas @mousewheel="mouseWheel" class="canvas" :id="'the-canvas'"></canvas>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+// pdfjs-dist版本为2.2.228
|
|
|
+import PDFJS from 'pdfjs-dist'
|
|
|
+// 目录
|
|
|
+import outline from './outline.vue'
|
|
|
+// 时间处理
|
|
|
+import moment from 'moment'
|
|
|
+export default {
|
|
|
+ name: 'pdflib',
|
|
|
+ components: {
|
|
|
+ outline
|
|
|
+ },
|
|
|
+ props: {
|
|
|
+ pdf_src: { type: String, default: '' }
|
|
|
+ },
|
|
|
+ data () {
|
|
|
+ return {
|
|
|
+ mode: 'phone',
|
|
|
+ flg: false,
|
|
|
+ // pdf放大系数
|
|
|
+ pdf_scale: 1,
|
|
|
+ // 所有页
|
|
|
+ pdf_pages: [],
|
|
|
+ // 当前页
|
|
|
+ is_page: 1,
|
|
|
+ pdfDoc: null,
|
|
|
+ // 目录大纲
|
|
|
+ outlineData: [],
|
|
|
+ // 目录宽度
|
|
|
+ outlineWidth: '100%',
|
|
|
+ // 是否显示目录
|
|
|
+ outlineShow: false,
|
|
|
+ // 记录当前时间
|
|
|
+ date: 0,
|
|
|
+ // 默认阻断阈值 毫秒
|
|
|
+ threshold: 200,
|
|
|
+ // 目录默认点击后打开或关闭
|
|
|
+ showAndHided: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted () {
|
|
|
+ this.init()
|
|
|
+ this.fingerInit()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ // 初始化屏幕触控
|
|
|
+ fingerInit () {
|
|
|
+ this.$finger(document.getElementsByTagName('canvas')[0], {
|
|
|
+ pinch: this.zoom,
|
|
|
+ swipe: this.swipeHandler
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 初始化应用 判断屏幕宽度
|
|
|
+ init () {
|
|
|
+ const result = window.matchMedia('(min-width: 500px)').matches
|
|
|
+ if (result) {
|
|
|
+ this.pdf_scale = 0.3
|
|
|
+ this.outlineWidth = '25%'
|
|
|
+ this.showAndHided = true
|
|
|
+ this.mode = 'pc'
|
|
|
+ }
|
|
|
+ this._loadFile(this.pdf_src)
|
|
|
+ },
|
|
|
+ // 初始化pdf 加载pdf文件
|
|
|
+ _loadFile (url) {
|
|
|
+ const loadingTask = PDFJS.getDocument(url)
|
|
|
+ loadingTask.promise
|
|
|
+ .then(async (pdf) => {
|
|
|
+ this.pdfDoc = pdf
|
|
|
+ this.pdf_pages = this.pdfDoc.numPages
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this._renderPage(this.is_page)
|
|
|
+ this.get_contents()
|
|
|
+ })
|
|
|
+ return null
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ console.log(err)
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 渲染pdf页
|
|
|
+ _renderPage(num) {
|
|
|
+ this.pdfDoc.getPage(num)
|
|
|
+ .then((page) => {
|
|
|
+ const canvas = document.getElementById('the-canvas')
|
|
|
+ const ctx = canvas.getContext('2d')
|
|
|
+ const dpr = window.devicePixelRatio || 1
|
|
|
+ const bsr = ctx.webkitBackingStorePixelRatio ||
|
|
|
+ ctx.mozBackingStorePixelRatio ||
|
|
|
+ ctx.msBackingStorePixelRatio ||
|
|
|
+ ctx.oBackingStorePixelRatio ||
|
|
|
+ ctx.backingStorePixelRatio || 1
|
|
|
+ const ratio = dpr / bsr
|
|
|
+
|
|
|
+ const viewport = page.getViewport({ scale: (screen.availWidth / page.getViewport({ scale: 1 }).width) * this.pdf_scale })
|
|
|
+ const { width, height } = viewport
|
|
|
+ canvas.width = width * ratio
|
|
|
+ canvas.height = height * ratio
|
|
|
+ canvas.style.width = `${width}px`
|
|
|
+ canvas.style.height = `${height}px`
|
|
|
+
|
|
|
+ ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
|
|
|
+ const renderContext = {
|
|
|
+ canvasContext: ctx,
|
|
|
+ viewport: viewport
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加翻书动画效果
|
|
|
+ const initialScale = 0.1
|
|
|
+ const targetScale = 1.0
|
|
|
+ let currentScale = initialScale
|
|
|
+
|
|
|
+ function animate() {
|
|
|
+ if (currentScale < targetScale) {
|
|
|
+ currentScale += 0.1 // 调整动画速度可以修改这个增量
|
|
|
+ ctx.clearRect(0, 0, canvas.width, canvas.height)
|
|
|
+ ctx.save()
|
|
|
+ ctx.scale(currentScale, 1)
|
|
|
+ page.render(renderContext)
|
|
|
+ ctx.restore()
|
|
|
+ requestAnimationFrame(animate)
|
|
|
+ } else {
|
|
|
+ this.flg = false // 动画结束后可以设置标志位等
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ animate()
|
|
|
+ })
|
|
|
+},
|
|
|
+ // _renderPage (num) {
|
|
|
+ // this.pdfDoc.getPage(num)
|
|
|
+ // .then((page) => {
|
|
|
+ // // 获取dom元素
|
|
|
+ // const canvas = document.getElementById('the-canvas')
|
|
|
+ // // 设置2D渲染
|
|
|
+ // const ctx = canvas.getContext('2d')
|
|
|
+ // // 设备像素比
|
|
|
+ // const dpr = window.devicePixelRatio || 1
|
|
|
+ // const bsr = ctx.webkitBackingStorePixelRatio ||
|
|
|
+ // ctx.mozBackingStorePixelRatio ||
|
|
|
+ // ctx.msBackingStorePixelRatio ||
|
|
|
+ // ctx.oBackingStorePixelRatio ||
|
|
|
+ // ctx.backingStorePixelRatio || 1
|
|
|
+ // const ratio = dpr / bsr
|
|
|
+ // // 参数为放大倍率
|
|
|
+ // const viewport = page.getViewport({ scale: (screen.availWidth / page.getViewport({ scale: 1 }).width) * this.pdf_scale })
|
|
|
+ // // 设置元素大小
|
|
|
+ // const { width, height } = viewport
|
|
|
+ // canvas.width = width * ratio
|
|
|
+ // canvas.height = height * ratio
|
|
|
+ // canvas.style.width = `${width}px`
|
|
|
+ // canvas.style.height = `${height}px`
|
|
|
+ // // 重绘cavas
|
|
|
+ // ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
|
|
|
+ // const renderContext = {
|
|
|
+ // canvasContext: ctx,
|
|
|
+ // viewport: viewport
|
|
|
+ // }
|
|
|
+ // // 渲染
|
|
|
+ // page.render(renderContext)
|
|
|
+ // this.flg = false
|
|
|
+ // })
|
|
|
+ // },
|
|
|
+ // 获取目录
|
|
|
+ async get_contents () {
|
|
|
+ const Outline = await this.pdfDoc.getOutline()
|
|
|
+ console.log(Outline)
|
|
|
+ this.outlineData = Outline
|
|
|
+ },
|
|
|
+ // 点击目录
|
|
|
+ async outline_item (e) {
|
|
|
+ const r = await this.pdfDoc.getDestination(e.dest)
|
|
|
+ const p = await this.pdfDoc.getPageIndex(r[0])
|
|
|
+ this.is_page = p + 1
|
|
|
+ this._renderPage(this.is_page)
|
|
|
+ this.outlineShow = this.showAndHided
|
|
|
+ },
|
|
|
+ // 上一页
|
|
|
+ pre_page () {
|
|
|
+ if (this.is_page <= 1) return
|
|
|
+ this.is_page--
|
|
|
+ this._renderPage(this.is_page)
|
|
|
+ },
|
|
|
+ // 下一页
|
|
|
+ next_page () {
|
|
|
+ if (this.is_page >= this.pdf_pages) return
|
|
|
+ this.is_page++
|
|
|
+ this._renderPage(this.is_page)
|
|
|
+ },
|
|
|
+ // 放大
|
|
|
+ zoom_big () {
|
|
|
+ this.pdf_scale += 0.1
|
|
|
+ this._renderPage(this.is_page)
|
|
|
+ },
|
|
|
+ // 缩小
|
|
|
+ zoom_small () {
|
|
|
+ this.pdf_scale -= 0.1
|
|
|
+ this._renderPage(this.is_page)
|
|
|
+ },
|
|
|
+ /*
|
|
|
+ /* 以下为移动端触控事件
|
|
|
+ */
|
|
|
+ // 缩放
|
|
|
+ zoom (e) {
|
|
|
+ const prevent = this.prevent(100)
|
|
|
+ if (!prevent) return
|
|
|
+ this.flg = true
|
|
|
+ const { zoom } = e
|
|
|
+ this.pdf_scale = this.pdf_scale * zoom
|
|
|
+ if (this.pdf_scale > 2) this.pdf_scale = 2
|
|
|
+ if (this.pdf_scale < 1) this.pdf_scale = 1
|
|
|
+ this.domScrol()
|
|
|
+ this._renderPage(this.is_page)
|
|
|
+ },
|
|
|
+ // 移动方向翻页
|
|
|
+ swipeHandler (evt) {
|
|
|
+ if (this.flg) return
|
|
|
+ const prevent = this.prevent(200)
|
|
|
+ if (!prevent) return
|
|
|
+ if (this.pdf_scale !== 0.3 && this.pdf_scale !== 1) {
|
|
|
+ this.domScrol()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (evt?.direction === 'Up' || evt?.direction === 'Left') this.next_page()
|
|
|
+ if (evt?.direction === 'Down' || evt?.direction === 'Right') this.pre_page()
|
|
|
+ },
|
|
|
+ // 移动端滚动位置
|
|
|
+ domScrol (e) {
|
|
|
+ const el = document.getElementsByClassName('canvasBox')[0]
|
|
|
+ if (e) {
|
|
|
+ el.scrollLeft = 0
|
|
|
+ el.scrollTop = 0
|
|
|
+ }
|
|
|
+ el.scrollLeft -= 0.1
|
|
|
+ el.scrollTop -= 0.1
|
|
|
+ },
|
|
|
+ // pc滚动事件
|
|
|
+ mouseWheel (e) {
|
|
|
+ if (this.pdf_scale.toFixed(1) > 0.3) return
|
|
|
+ const prevent = this.prevent(200)
|
|
|
+ if (!prevent) return
|
|
|
+ if (e.wheelDelta || e.detail) {
|
|
|
+ // 当鼠标滚轮向上滚动时
|
|
|
+ if (e.wheelDelta > 0 || e.detail < 0) {
|
|
|
+ this.pre_page()
|
|
|
+ }
|
|
|
+ // 当鼠标滚轮向下滚动时
|
|
|
+ if (e.wheelDelta < 0 || e.detail > 0) {
|
|
|
+ this.next_page()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 阻断连续调用
|
|
|
+ prevent (num) {
|
|
|
+ const date = moment().valueOf()
|
|
|
+ if (date - this.data < num ?? this.threshold) return false
|
|
|
+ this.data = date
|
|
|
+ return true
|
|
|
+ },
|
|
|
+ // 动画
|
|
|
+ animation () {
|
|
|
+ console.log('动画')
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="less">
|
|
|
+.pdf {
|
|
|
+ width: 100vw;
|
|
|
+ height: 100vh;
|
|
|
+}
|
|
|
+.canvasBox {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ overflow: auto;
|
|
|
+ background: #000;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ // justify-content: center;
|
|
|
+}
|
|
|
+.canvas {
|
|
|
+ margin: 0 auto;
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+.btnbox {
|
|
|
+ position: fixed;
|
|
|
+ top: 10px;
|
|
|
+ left: 5%;
|
|
|
+ width: 90%;
|
|
|
+ height: 5vh;
|
|
|
+ .left {
|
|
|
+ margin-left: 40%;
|
|
|
+ }
|
|
|
+ .right {
|
|
|
+ margin-right: 40%;
|
|
|
+ }
|
|
|
+}
|
|
|
+.outline {
|
|
|
+ height: 95vh;
|
|
|
+ top: 5vh;
|
|
|
+ position: fixed;
|
|
|
+ left: 0;
|
|
|
+}
|
|
|
+.outline ::-webkit-scrollbar {
|
|
|
+ width: 0px; /*对垂直流动条有效*/
|
|
|
+ height: 0px; /*对水平流动条有效*/
|
|
|
+}
|
|
|
+</style>
|