前端预览pdf

623 阅读1分钟

基于版本 2.0.489 实现预览pdf

npm install pdfjs-dist@2.0.489

如果选用了其他版本,在调用getDocument的时候报错,建议直接打印一下 PDFJS 这个方法,因为在不同的大版本时,会有不同的调用方法

 首先引入对应的依赖依赖以及数据流

<script>
  import PDFJS from "pdfjs-dist";
  import { baseData } from "./base64Data"; // 这里引入pdf数据流,如果是直接拿远程pdf文件,会出现跨域的情况.可以让后端转化成base64数据流返回  
</script>

创建canvas画布,每一页的pdf对应一个canvas.

  <template>
    <div>
      <canvas :id="'canvasPdf'+page" v-for="page in count" :key="page"></canvas>
    </div>
  </template>
	methods: {
    // 页面初始化方法
      init() {
      	const _this = this;
      	let pdfBase64Data = atob(baseData); //解码base64编码字符串
      	PDFJS.getDocument({ data: pdfBase64Data }).then(res => {
        _this.pdfDoc = res;
        _this.count = res.numPages; //pdf的页数
        _this.$nextTick(() => {
          _this.renderPage(1);
        });
       });
      },
      renderPage(num) {
      	const _this = this;
        _this.pdfDoc.getPage(num).then((page) => {
          // 拿到对应的canvas的id
          let canvas = document.getElementById("canvasPdf" + 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 scale = _this.pdfWidth / page.getViewport(1).width;
          let viewport = page.getViewport(scale);
          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);
          let renderContext = {
            canvasContext: ctx,
            viewport: viewport,
          };
          page.render(renderContext);
          // 绘制pdf做递归处理,当pdf的页数小于传入的页数,跳出递归
          if (_this.count > num) {
            setTimeout(() => {
              _this.renderPage(num + 1);
            }, 500);
          }
        });
      }
   }

完整代码支持些许功能,参数如下.往大佬指点

属性
名字必输类型默认值说明
pdfUrltruestring传入pdf的地址url
pdfWidthtruenumber显示pdf内容的宽
pdfHeighttruenumber显示pdf内容的高
pdfTitlefalsestringpdf头部标题
isNeedButtonfalsebooleanfalse是否需要按钮
isCountDownfalsebooleanfalse当isNeedButton为true生效,是否需要倒计时
countDownfalsenumber30倒计事件(秒)
事件
名字事件说明备注
on-clickclick点击按钮,返回值
caseErrorerror接口请求失败/渲染出错返回错误信息
<template>
  <div class="pdf" :style="{'width': pdfWidth + 'px', 'height': pdfHeight + 'px'}">
    <div class="pdf-title" v-if="pdfTitle">{{pdfTitle}}</div>
    <div class="pdf-content">
      <canvas :id="'canvasPdf'+page" v-for="page in count" :key="page"></canvas>
    </div>
    <div v-if="isNeedButton">
      <div v-if="!isCountDown" class="bottomBtn" :style="buttonActive" @click="submit(true)">确定</div>
      <div
        @click="submit(false)"
        v-if="isCountDown"
        class="bottomBtn"
        :style="countDown == 0 ? buttonActive : buttonDisabled"
      >确定{{` ${countDown}`}}</div>
    </div>
  </div>
</template>

<script>
import PDFJS from "pdfjs-dist";
import { baseData } from "./base64Data"; // 此处为引入的base64数据流,需要更改
export default {
  props: {
    // PDF内容宽
    pdfWidth: {
      type: Number,
      default: 0,
    },
    // PDF内容高
    pdfHeight: {
      type: Number,
      default: 0,
    },
    // 是否需要按钮
    isNeedButton: {
      type: Boolean,
      default: false,
    },
    // 是否按钮需要倒计时
    isCountDown: {
      type: Boolean,
      default: false,
    },
    // 倒计时
    countDown: {
      type: Number,
      default: 30,
    },
    pdfUrl: {
      type: String,
      defaut: "",
    },
    pdfTitle: {
      type: String,
      defaut: "",
    },
  },
  data() {
    return {
      pdfDoc: "",
      count: 0,
      buttonActive: {
        background: "#ea2c1d",
        color: "white",
      },
      buttonDisabled: {
        background: "#D3D3D3",
        color: "#696969",
      },
    };
  },
  watch: {
  	// 按钮倒计时
    countDown(val) {
      if (val == 0) {
        clearInterval(this.setIntervalID);
        this.countDown = "";
      }
    },
  },
  methods: {
    init() {
      const _this = this;
      let pdfBase64Data = atob(baseData);
      PDFJS.getDocument({ data: pdfBase64Data }).then(res => {
        this.setIntervalID = setInterval(() => {
          _this.countDown--;
        }, 1000);
        _this.pdfDoc = res;
        _this.count = res.numPages;
        _this.$nextTick(() => {
          _this.renderPage(1);
        });
      });
    },
    // 渲染pdf
    renderPage(num) {
      const _this = this;
      _this.pdfDoc.getPage(num).then(page => {
        let canvas = document.getElementById("canvasPdf" + 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;
        console.log("screen.availWidth", screen.availWidth);
        let scale = _this.pdfWidth / page.getViewport(1).width;
        console.log("scale", scale);
        let viewport = page.getViewport(scale);
        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);
        let renderContext = {
          canvasContext: ctx,
          viewport: viewport,
        };
        page.render(renderContext);
        if (_this.count > num) {
          setTimeout(() => {
            _this.renderPage(num + 1);
          }, 500);
        }
      });
    },
    submit(flag) {
      if (flag) {
        this.$emit("on-click", true);
      } else {
        if (this.countDown > 0) {
          return;
        } else {
          this.$emit("on-click", true);
        }
      }
    },
  },
  created() {
    this.init();
  },
};
</script>

<style scoped>
.pdf {
  overflow-y: scroll;
}
.pdf-title {
  margin: 10px 0;
  text-align: center;
}
.pdf-content {
  display: flex;
  flex-direction: column;
}
.bottomBtn {
  width: 80%;
  height: 45px;
  margin: 15px 0;
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  text-align: center;
  line-height: 45px;
}
</style>