前言:

demo,请参考demo地址

版本:

 "vue": "^2.6.11",
  "pdfjs-dist": "^2.6.347"

npm上关于PDF.js的包有两个,pdf.jspdfjs-dist; 本文用到的是pdfjs-dist ;PDF.js提供了一个预览页面viewer.html来实现pdf的在线预览功能;故可将pdf.js作为静态资源下载到项目中 ,只需更改为pdf文件存放服务器的地址即可在vue项目中实现pdf的在线展示。

npm install方式使用pdf.js: 需根据需求自己写样式,实现相关功能。

以静态资源方式使用: 将pdf下载到本地项目中,以静态资源方式使用,通过 pdf.js 提供的 viewer.html 文件来展示服务器上的pdf文件,无须自己设置样式(已有pdf.js的全套样式和相关功能,不需要的地方可通过更改源码等方式自行去掉)

一、安装插件

yarn add pdfjs-dist

二、在组件中测试是否引入成功

const PDFJS = require("pdfjs-dist");
console.log("成功:",PDFJS )

打印结果:
在这里插入图片描述

三、踩坑合集

1. 需手动设置workerSrc

报错信息如下: Uncaught SyntaxError: Unexpected token '<' Cannot read property 'WorkerMessageHandler' of undefined
在这里插入图片描述
注意:此处,项目中应用到的pdf.js的版本为2.5.207

 "vue": "^2.6.11",
"pdfjs-dist": "^2.5.207",

解决方法:需手动设置workerSrc

const PDFJS = require("pdfjs-dist");
PDFJS.GlobalWorkerOptions.workerSrc =
"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.5.207/pdf.worker.js";

2. 注意 :pdf.js版本升级后,它的getDocument方法返回的是一个promise

参考从前的例子写的原代码:

  loadFile(url) {
      let loadingTask = PDFJS.getDocument(url);
      loadingTask.then(pdf => { // 老版本该处是正确写法
       ......
      });
    },

报错信息:TypeError: loadingTask.then is not a function
在这里插入图片描述
解决办法:注意promise:

loadFile(url) {
      let loadingTask = PDFJS.getDocument(url);
      loadingTask.promise.then(pdf => { // 注意该行的promise,版本升级后,getDocument方法返回promise
    	......
      });
    },

四、预览整个pdf文件文档 示例

<template>
  <div class="preview-pdf">
    <h1>PDF在线预览</h1>
    <div :style="`margin:0 auto;width:${pdfWidth};`">
      <canvas
        v-for="page in pdfPages"
        :key="page"
        :id="'pdfCanvas' + page"
      ></canvas>
    </div>
  </div>
</template>
<script>
const PDFJS = require("pdfjs-dist");
PDFJS.GlobalWorkerOptions.workerSrc = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.5.207/pdf.worker.js";
export default {
  data() {
    return {
      pdfPages: [], // 页数
      pdfWidth: "", // 宽度
      pdfSrc: "", // 地址
      pdfDoc: "", // 文档内容
      pdfScale: 1.0 // 放大倍数
    };
  },
  mounted() {
    this.getPdfUrl();
  },
  methods: {
    getPdfUrl() {
      // todo 请求后台,获取pdf的url,这里用的是线上的地址
      this.pdfSrc =
        "https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf";
      this.loadFile(this.pdfSrc);
    },
    loadFile(url) {
      let loadingTask = PDFJS.getDocument(url);
      loadingTask.promise.then(pdf => {
        this.pdfDoc = pdf;
        this.pdfPages = pdf.numPages;
        this.$nextTick(() => { 
          this.renderPage(1);
        });
      });
    },
    renderPage(num) {
      const that = this;
      this.pdfDoc.getPage(num).then(page => {
        let canvas = document.getElementById("pdfCanvas" + num);
        let ctx = canvas.getContext("2d");
        let dpr = window.devicePixelRatio || 1;
        let bsr =
          ctx.webkitBackingStorePixelRatio ||
          ctx.mozBackingStorePixelRatio ||
          ctx.msBackingStorePixelRatio ||
          ctx.oBackingStorePixelRatio ||
          ctx.backingStorePixelRatio ||
          1;
        let ratio = dpr / bsr;
        let viewport = page.getViewport({ scale: this.pdfScale });
        canvas.width = viewport.width * ratio;
        canvas.height = viewport.height * ratio;

        canvas.style.width = viewport.width + "px";

        that.pdfWidth = viewport.width + "px";

        canvas.style.height = viewport.height + "px";

        ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
        // 将 PDF 页面渲染到 canvas 上下文中
        let renderContext = {
          canvasContext: ctx,
          viewport: viewport
        };
        page.render(renderContext);
        if (this.pdfPages > num) {
          this.renderPage(num + 1);
        }
      });
    }
  }
};
</script>
<style lang="scss">
.preview-pdf {
  h1 {
    margin: 30px auto;
    text-align: center;
    font-family: "宋体";
    letter-spacing: 2px;
  }
}
</style>

上述代码效果如图(英文部分是pdf文件内容):

在这里插入图片描述

五、上一页下一页,示例

<template>
  <div class="preview-pdf">
    <h1>PDF在线预览</h1>
    <div style="text-align:center;">
      <button id="prev" @click="onPrePage">
        Previous
      </button>
      <button id="next" @click="onNextPage">Next</button>

      <span>
        Page:
        <span id="page_num">{{ currentPage }}</span>
        /
        <span id="page_count">{{ totalPages }}</span></span
      >
    </div>
    <div
      :style="`margin:0 auto;width:${pdfWidth};`"
      v-loading="isLoading"
      element-loading-text="加载中..."
    >
      <canvas id="pdfCanvas"></canvas>
    </div>
  </div>
</template>
<script>
const PDFJS = require("pdfjs-dist");
PDFJS.GlobalWorkerOptions.workerSrc =
  "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.5.207/pdf.worker.js";
export default {
  data() {
    return {
      totalPages: [], // 总页数
      currentPage: 1, // 当前页
      pageRendering: false, // 是否正在渲染
      pageNumPending: null, // 待处理页码
      pdfWidth: "", // 宽度
      pdfSrc: "", // 地址
      pdfDoc: null, // 文档内容
      pdfScale: 1.0, // 放大倍数
      isLoading: false // 文档是否正在加载
    };
  },
  mounted() {
    this.getPdfUrl();
  },
  watch: {
    pageRendering(newVal, old) {
      this.isLoading = newVal;
    }
  },
  methods: {
    getPdfUrl() {
      // todo 请求后台,获取pdf的url,注意这里不能是本地的pdf文件
      this.pdfSrc =
        "https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf";
      this.loadFile(this.pdfSrc);
    },
    loadFile(url) {
      //通过promise获取页面
      let loadingTask = PDFJS.getDocument(url);
      this.pageRendering = true;
      loadingTask.promise.then(pdf => {
        this.pdfDoc = pdf;
        this.totalPages = pdf.numPages;
        this.$nextTick(() => {
          this.renderPage(1); // 初始/首页渲染
        });
      });
    },
    renderPage(num) {
      this.currentPage = num; // 更新当前页码
      this.pdfDoc.getPage(num).then(page => {
        let canvas = document.getElementById("pdfCanvas");
        let ctx = canvas.getContext("2d");
        let dpr = window.devicePixelRatio || 1;
        let bsr =
          ctx.webkitBackingStorePixelRatio ||
          ctx.mozBackingStorePixelRatio ||
          ctx.msBackingStorePixelRatio ||
          ctx.oBackingStorePixelRatio ||
          ctx.backingStorePixelRatio ||
          1;
        let ratio = dpr / bsr;
        let viewport = page.getViewport({ scale: this.pdfScale });
        canvas.width = viewport.width * ratio;
        canvas.height = viewport.height * ratio;

        canvas.style.width = viewport.width + "px";

        this.pdfWidth = viewport.width + "px";

        canvas.style.height = viewport.height + "px";

        ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
        // 将 PDF 页面渲染到 canvas 上下文中
        let renderContext = {
          canvasContext: ctx,
          viewport: viewport
        };
        let renderTask = page.render(renderContext);
        renderTask.promise.then(() => {
          this.pageRendering = false;
          if (this.pageNumPending === null) return;
          this.renderPage(this.pageNumPending);
          this.pageNumPending = null;
        });
      });
    },
    onPrePage() {
      // 上一页
      if (this.currentPage <= 1) return;
      this.currentPage--;
      this.queueRender(this.currentPage);
    },
    onNextPage() {
      // 下一页
      if (this.currentPage >= this.totalPages) return;
      this.currentPage++;
      this.queueRender(this.currentPage);
    },
    queueRender(num) {
      //渲染等待;如果正在进行另一个页面渲染,请等待渲染完成。 否则,立即执行渲染
      !this.pageRendering ? this.renderPage(num) : (this.pageNumPending = num);
    }
  }
};
</script>
<style lang="scss">
.preview-pdf {
  h1 {
    margin: 30px auto;
    text-align: center;
    font-family: "宋体";
    letter-spacing: 2px;
  }
}
</style>

在这里插入图片描述

参考:

  1. pdfjs–examples
  2. 2020 pdfjs在vue中的使用
Logo

前往低代码交流专区

更多推荐