前言:
demo,请参考: demo地址
版本:
"vue": "^2.6.11",
"pdfjs-dist": "^2.6.347"
npm
上关于PDF.js
的包有两个,pdf.js
和pdfjs-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 )
报错信息如下: 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";
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
......
});
},
<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>
参考: