当前位置: 首页 > 工具软件 > pdfjs-dist > 使用案例 >

vue3.0使用pdfjs-dist查看pdf文件

翁钧
2023-12-01

pdf预览组件


前情:项目要的急,就简单的能看pdf文件,没有其他功能

使用的 pdfjs-dist 版本号:2.5.207

接下来,直接上代码 ♫


封装组件

<template>
  <template v-for="item in pageNum" :key="item">
    <canvas :id="`pdf-canvas-${item}`" class="pdf-page" />
  </template>
</template>

<script>
import { reactive, toRefs, nextTick, watchEffect } from 'vue'
import * as pdfjs from 'pdfjs-dist'
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';
import { Toast } from 'vant'

pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker

export default {
  name: 'PdfViewer',
  props: {
    url: {type: String, default: ''} // pdf文件路径
  },
  setup (props, { emit }) {
    const state = reactive({
      pageNum: 0,
      pdfCtx: null
    })

    const resolvePdf = (url) => {
      const loadingTask = pdfjs.getDocument(url)
      loadingTask.promise.then(pdf => {
        state.pdfCtx = pdf
        state.pageNum = pdf.numPages
        nextTick(() => {
          renderPdf()
        })
      })
    }

    const renderPdf = (num = 1) => {
      state.pdfCtx.getPage(num).then(page => {
        const canvas = document.getElementById(`pdf-canvas-${num}`)
        const ctx = canvas.getContext('2d')
        const viewport = page.getViewport({ scale: 1 })
        // 画布大小,默认值是width:300px,height:150px
        canvas.height = viewport.height
        canvas.width = viewport.width
        // 画布的dom大小, 设置移动端,宽度设置铺满整个屏幕
        const clientWidth = document.body.clientWidth
        canvas.style.width = clientWidth + 'px'
        // 根据pdf每页的宽高比例设置canvas的高度
        canvas.style.height = clientWidth * (viewport.height / viewport.width) + 'px'
        page.render({
          canvasContext: ctx,
          viewport
        })
        if (num < state.pageNum) {
          renderPdf(num + 1)
        } else {
          emit('onRendered')
		  Toast.clear(); // 取消加载loading
        }
      })
    }

    watchEffect(() => {
      if (props.url) {
      	// 展示加载loading
        Toast.loading({
          message: '加载中...',
          overlay: true,
          forbidClick: true,
          duration: 0,
        });

        resolvePdf(props.url)
      }
    })
    return {
      ...toRefs(state)
    }
  }
}
</script>

组件注册

这里是全局注册组件,main.js中添加下面代码 引入组件

详细方法可看我的文章vue3.0全局注册自定义组件及使用

import PdfViewer from "./components/PdfViewer.vue"

app.component('PdfViewer', PdfViewer)

使用

pdfUrl中传入pdf文件地址

<PdfViewer :url="pdfUrl"></PdfViewer>

参考文章
https://www.52pojie.cn/thread-1391808-1-1.html





增加缩放功能(2021-12-15更新)

有了些空闲时间,想完善下pdf预览组件
看了网上的文章,都需要时间慢慢研究下
空闲时间不多,我就先用我想到的方法,实现下功能叭~ ♫


增加了工具栏,有页面缩放功能,和当前页页码展示

缩放功能关键点:
pdfjs的scale设置为需要放大的最大值;
使用css进行缩放


组件

<template>
  <!-- 自由发挥想象的工具栏 -->
  <div class="tool-bar" v-show="totalPageNum != 0">
  	<div class="pageNum">{{currentPageNum}} / {{totalPageNum}}页</div>
    <span class="txt">放大</span>
    <div class="box">
      <div class="mask" v-show="zoom == maxZoom"></div>
      <van-icon name="plus" size="18" @click="zoomChange('plus')"/>
    </div>
    <span class="txt">缩小</span>
    <div class="box">
      <div class="mask" v-show="zoom == minZoom"></div>
      <van-icon name="minus" size="18" @click="zoomChange('minus')"/>
    </div>
  </div>
  <!-- 工具栏 end -->

  <div class="pdf-viewer" @scroll="onScroll">
    <template v-for="item in totalPageNum" :key="item">
      <canvas class="pdf-item" :id="`pdf-canvas-${item}`" :style="{zoom: zoom/10}" />
    </template>
  </div>
</template>

<script>
import { reactive, toRefs, nextTick, watchEffect } from 'vue'
import * as pdfjs from 'pdfjs-dist'
// import {GlobalWorkerOptions, getDocument} from 'pdfjs-dist'
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';
import { Toast } from 'vant'

pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker

export default {
  name: 'PdfViewer',
  props: {
    url: {type: String, default: ''} // pdf文件路径
  },
  setup (props, { emit }) {
    const state = reactive({
      pdfCtx: null,

      currentPageNum: 0, // 当前页
      totalPageNum: 0,
      zoom: 10, // 目前暂时采用css方式缩放页面
      minZoom: 10, // 缩放最小值 一倍
      maxZoom: 50, // 缩放最大值 五倍
    })

    const resolvePdf = (url) => {
      const loadingTask = pdfjs.getDocument(url)
      loadingTask.promise.then(pdf => {
        state.pdfCtx = pdf
        state.totalPageNum = pdf.numPages
        state.currentPageNum = 1

        // 动态计算scale
        pdf.getPage(1).then(res => {
          let boxWidth = document.body.clientWidth - 20
          const [x1, , x2] = res._pageInfo.view;
          const pageWidth = x2 - x1;
          state.scale = boxWidth*(state.maxZoom/10) / pageWidth
        })
        nextTick(() => {
          renderPdf()
        })
      })
    }

    const renderPdf = (num = 1) => {
      state.pdfCtx.getPage(num).then(page => {
        const canvas = document.getElementById(`pdf-canvas-${num}`)
        const ctx = canvas.getContext('2d')
        const viewport = page.getViewport({ scale: state.scale })
        // 画布大小,默认值是width:300px,height:150px
        canvas.width = viewport.width
        canvas.height = viewport.height
        // 画布的dom大小, 设置移动端,宽度设置铺满整个屏幕
        const clientWidth = document.body.clientWidth - 20
        canvas.style.width = clientWidth + 'px'
        // 根据pdf每页的宽高比例设置canvas的高度
        canvas.style.height = clientWidth * (viewport.height / viewport.width) + 'px'
        page.render({
          canvasContext: ctx,
          viewport
        })
        if (num < state.totalPageNum) {
          renderPdf(num + 1)
        } else {
          emit('onRendered')
		      Toast.clear();
        }
      })
    }

	const zoomChange = (value) => {
      state.zoom = value == 'plus' ? state.zoom + 5 : state.zoom - 5

      let pdfViewer = document.getElementsByClassName('pdf-viewer')[0]
      let pages = document.getElementsByClassName('pdf-item')
      pdfViewer.scrollTop = pages[state.currentPageNum-1].offsetTop*state.zoom/10
    }

    const onScroll = (e) => {
      let pages = document.getElementsByClassName('pdf-item')
      for (let i=0; i<state.totalPageNum; i++) {
        let offset = e.target.offsetHeight/2-100 // 距顶部的距离(当前页在滚动到页面可视区的哪个位置时更改currentPageNum)
        if (e.target.scrollTop >= pages[i].offsetTop*state.zoom/10 - offset ) {
          if ((pages[i+1] && e.target.scrollTop < pages[i+1].offsetTop*state.zoom/10 - offset) || !pages[i+1]) {
            state.currentPageNum = i+1 
          }
        }
      }
    }

    watchEffect(() => {
      if (props.url) {
        Toast.loading({
          message: '文件加载中...',
          overlay: true,
          forbidClick: true,
          duration: 0,
        });

        resolvePdf(props.url)
      }
    })
    return {
      ...toRefs(state),
      zoomChange,
      onScroll
    }
  }
}
</script>

<style lang="less" scoped>
  .pdf-viewer {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 10px 10px 50px;
    overflow: scroll;
    background-color: #f5f5f5;
  }
  // 放飞自我的工具栏
  .tool-bar {
    z-index: 2;
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 45px;
    line-height: 45px;
    color: #fff;
    padding: 0 20px;
    background-color: rgba(85, 85, 85, 0.6);
    display: flex;
    justify-content: flex-end;
    .pageNum {
      flex: 1;
      font-size: 17px;
    }
    .txt {
      font-size: 16px;
      padding-right: 5px;
      margin-left: 20px;
    }
    .box {
      position: relative;
      margin-top: 5px;
      width: 35px;
      height: 35px;
      line-height: 35px;
      text-align: center;
      background-color: #3476FE;
      border-radius: 50%;
      .van-icon {
        vertical-align: middle;
        font-weight: bold;
      }
      .mask {
        z-index: 1;
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(109, 109, 109, .7);
        border-radius: 50%;
      }
    }
    // 工具栏 end
  }
</style>

使用

父级要有一个整屏高的外层

<div style="position: relative;min-height: calc( 100vh - 50px);">
...
	<PdfViewer :url="pdfUrl"></PdfViewer>
...
</div>
 类似资料: