效果展示:

首先我的版本是:

 "vue": "^3.3.4",

 "vite": "^4.2.1",

"pdfjs-dist": "^4.0.379",

然后安装 :

npm i pdfjs-dist

如果遇到 报错:

Top-level await is not available in the configured target environment ("chrome87", "edge88", "es2020", "firefox78", "safari14" + 2 overrides) node_modules/pdfjs-dist/build/pdf.mjs:17349:53: 17349 │ /******/ __webpack_exports__ = globalThis.pdfjsLib = await (globalThis.pdfjsLibPromise = __webpack_exports__);

  • 原因:vite 不支持顶级的 async/await 语法,需要安装插件做兼容
  • 解决:vite.config.ts 安装并引入 topLevelAwait
  • npm install vite-plugin-top-level-await -D

    然后 找到对应位置:   // vite.config.ts 文件
    import topLevelAwait from 'vite-plugin-top-level-await'

    export default ({ mode }: any) => {
        return defineConfig({
            plugins: [
                topLevelAwait({
                    promiseExportName: '__tla',
                    promiseImportName: (i) => `__tla_${i}`
                })
            ],
        })
    }

具体完整实现代码:

pdf组件

<template>
  <div class="pdf-container">
    <canvas  v-for="pdfIndex in pdfPages" :id="`pdf-canvas-${pdfIndex}`" :key="pdfIndex" />
  </div>
</template>
<script setup lang="ts">
import * as PDFJS from 'pdfjs-dist/legacy/build/pdf.mjs'
import * as PdfWorker from 'pdfjs-dist/legacy/build/pdf.worker.mjs'
import { nextTick, ref, Ref, watch } from 'vue'
import { isEmpty, debounce } from 'lodash-es'


const props: any = defineProps({
  pdf: {
    required: true
  }
})

let pdfDoc: any = null
const pdfPages: Ref = ref(0)
const pdfScale: Ref = ref(1.3)
const loadFile = async (url: any) => {
  // 设定pdfjs的 workerSrc 参数
  PDFJS.GlobalWorkerOptions.workerSrc = PdfWorker
  const loadingTask = PDFJS.getDocument(url)
  loadingTask.promise.then(async (pdf: any) => {
    pdfDoc = pdf // 保存加载的pdf文件流
    pdfPages.value = pdfDoc.numPages // 获取pdf文件的总页数
    await nextTick(() => {
      renderPage(1) // 将pdf文件内容渲染到canvas
    })
  }).catch((error: any) => {
    //可以用自己组件库弹出提示框
    console.log(error)
  })
}

const renderPage = (num: any) => {
  pdfDoc.getPage(num).then((page: any) => {
    page.cleanup()
    const canvas: any = document.getElementById(`pdf-canvas-${num}`)
    if (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: pdfScale.value })
      canvas.width = viewport.width * ratio
      canvas.height = viewport.height * ratio
      canvas.style.width = viewport.width + 'px'
      canvas.style.height = viewport.height + 'px'
      ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
      const renderContext = {
        canvasContext: ctx,
        viewport: viewport
      }
      page.render(renderContext)
      if (num < pdfPages.value) {
        renderPage(num + 1)
      }
    }
  })
}

const debouncedLoadFile = debounce((pdf: any) => loadFile(pdf), 1000)
watch(() => props.pdf, (newValue: any) => {
  !isEmpty(newValue) && debouncedLoadFile(newValue)
}, {
  immediate: true
})
</script>
<style scoped lang="less">
.pdf-container {
  width: 100%;
  height: 100%;
  overflow: auto; /* 启用滚动条 */
}

canvas {
  width: 100%;
  max-height: 100vh; /* 设置最大高度为视口高度 */
}
</style>

 父组件:

  <!-- pdf展示 -->
      <div class="leftMain">
        <PdfView :pdf="jsPdf" style="width: 100%;height:100%;"></PdfView>
      </div>

。。。
import jsPdf from '../../../assets/pdf/pdf1.pdf'

如果需要使用点击跳页的话,用一个变量控制 页码,然后在canvas上换成变量,每次跳页就+-数字就好,位置可以自己定位到需要位置。

注意:

不同vite版本引用路径可能不同:

如果不知道在哪,可以点开node_modules找到pdfjs-dist对应去改一下就好

版本会引起报各种错,需要注意,实在不行复制错误搜索就好。。。

当前时间是可以使用的,刚实现过,后续如果不可以用了,可能是安装东西的版本迭代了。。重新安装的时候版本不同了

优化空间

不分页的话加载过大文件时速度很慢,可以懒加载 ,也可以分页

样式美化

 借鉴文章:import引入pdfjs-dist报错Top-level await is not available、No "GlobalWorkerOptions.workerSrc"等 - ziChin - 博客园 (cnblogs.com)

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐