为了兼容IE11,选型了pdfjs-dist;本来用了vue-pdf,但是IE11上面会出现文字丢失情况,只能换方案。
实现了 上下翻页、放大、缩小、输入页码跳转页面、左侧缩略图等功能
pdf文件来自后端接口BLOB
<template>
<div>
<div class="toolbar">
<div>
<span
class="backBtn"
@click="goBack"
><i class="el-icon-arrow-left" />返回</span>
</div>
<div style="padding-left: 200px">
<el-button
type="text"
icon="el-icon-arrow-left"
size="mini"
@click="prePage"
>
上一页
</el-button>
<span class="pageNum">
<el-input v-model="currentPage" /> / {{ pageTotalNum }}
</span>
<el-button
type="text"
size="mini"
@click="nextPage"
>
下一页<i class="el-icon-arrow-right el-icon--right" />
</el-button>
<el-button
type="text"
size="mini"
@click="scaleD"
>
放大
</el-button>
<el-button
type="text"
size="mini"
@click="scaleX"
>
缩小
</el-button>
</div>
<div />
</div>
<div class="container">
<el-scrollbar class="scrollBarWrapper">
<div
id="thumbnailContainer"
ref="scrollThumWrapper"
/>
</el-scrollbar>
<div class="pdfContainerWrapper">
<div id="pdfContainer" />
</div>
</div>
</div>
</template>
<style>
.scrollBarWrapper {
height: 1305px;
}
.scrollBarWrapper .el-scrollbar__wrap {
overflow-x: hidden;
overflow-y: scroll;
}
.toolbar .el-input {
width: 32px;
}
.toolbar .el-input .el-input__inner {
background-color: #555555;
color: #ffffff;
width: 32px;
padding: 0;
height: 20px;
text-align: center;
line-height: 18px;
}
#thumbnailContainer > .thumItem {
margin: 10px 0;
cursor: pointer;
}
.thumItem.active {
border: 5px solid #87cef0;
}
</style>
<style scoped>
.toolbar {
font-size: 14px;
height: 46px;
width: 100%;
background-color: #444444;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 50px 0 10px;
border-bottom: 1px solid #888888;
}
.backBtn {
color: #409eff;
cursor: pointer;
}
.pageNum {
height: 29px;
line-height: 29px;
color: #409eff;
margin: 0 20px;
}
.container {
display: flex;
background-color: #555555;
position: relative;
}
#thumbnailContainer {
display: flex;
flex-direction: column;
align-items: center;
width: 260px;
background-color: #666666;
}
.pdfContainerWrapper {
width: calc(100% - 260px);
padding: 20px;
display: flex;
justify-content: center;
}
</style>
<script>
import PDF from 'pdfjs-dist';
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';
PDF.GlobalWorkerOptions.workerSrc = pdfjsWorker;
export default {
name: 'IEPreview',
data() {
return {
pdfInstance: null, // pdf实例
pageTotalNum: 0, // 拿到的pdf总页数
scale: 1.5,
maxScale: 2.5,
minScale: 0.9,
scaleStep: 0.2,
thumScale: 0.35,
currentPage: 1, // 拿到的pdf当前页数,
prevPage: 1, // 记录上一页;兼容输入框跳页情况
thumItemActive: 'thumItem active',
pdfUrld: '' // pdf文件路径变量
};
},
watch: {
currentPage(newVal) {
const num = Number(newVal);
if (typeof num === 'number' && !Number.isNaN(num) && num > 0 && num <= this.currentPage) {
this.goToPage(num);
}
}
},
beforeMount() {
this.loading = this.$loading({
lock: true,
target: '.pdfViewerContent',
text: '加载中',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
},
mounted() {
const folder = /folder=(.*)&/.exec(location.hash)[1]?.trim();
const fileName = /fileName=(.*)/.exec(location.hash)[1]?.trim();
this.fileParent = folder;
const url = `${process.env.VUE_APP_BASE_API}/policy/file/info?folder=${folder}&filename=${fileName}&ts=${(new Date()).valueOf()}`;
this.init(url);
},
methods: {
// 翻页同时缩略图滚动
thumbnailScroll() {
if (this.currentPage === 1) {
return false;
}
this.$refs.scrollThumWrapper.parentNode.parentNode.scrollTop = (this.currentPage > 9 ? this.currentPage : this.currentPage - 1) * 300;
},
// 跳页
goToPage(page) {
this.pdfContainer.children[this.prevPage - 1].style.display = 'none';
this.thumbnailContainer.children[(this.prevPage - 1) * 2].className = 'thumItem';
this.currentPage = page;
this.prevPage = page;
this.pdfContainer.children[page - 1].style.display = 'inline-block';
this.thumbnailContainer.children[(page - 1) * 2].className = this.thumItemActive;
this.thumbnailScroll();
},
// 回到列表页
goBack() {
if (['company', 'it', 'form'].includes(this.fileParent)) {
this.$router.push(`/policy/${this.fileParent}`);
return;
}
},
// 上一页
prePage() {
let page = this.currentPage;
page = page > 1 ? page - 1 : this.pageTotalNum;
this.goToPage(page);
},
// 下一页
nextPage() {
let page = this.currentPage;
page = page < this.pageTotalNum ? page + 1 : 1;
this.goToPage(page);
},
/**
* 放大或者缩小之后,重新渲染
*/
afterChangeScale() {
const pdfContainer = this.pdfContainer;
while (pdfContainer.firstChild) {
pdfContainer.removeChild(pdfContainer.firstChild);
}
for (let pageNum = 1; pageNum <= this.pageTotalNum; pageNum++) {
this.pdfInstance.getPage(pageNum).then((page) => {
this.createCanvas({
page,
container: pdfContainer,
isThum: false,
pageNum
});
if (pageNum === this.pageTotalNum) {
pdfContainer.children[0].style.display = 'none';
pdfContainer.children[this.currentPage - 1].style.display = 'inline-block';
}
});
}
},
// 放大
scaleD() {
this.scale += this.scaleStep;
if (this.scale < this.maxScale) {
this.afterChangeScale();
} else {
this.$message.info('已放大到最大比例');
this.scale -= this.scaleStep;
}
},
// 缩小
scaleX() {
this.scale -= this.scaleStep;
if (this.scale > this.minScale) {
this.afterChangeScale();
} else {
this.$message.info('已缩小到最小比例');
this.scale += this.scaleStep;
}
},
renderPDF({ page, canvas, viewport }) {
const context = canvas.getContext('2d');
const renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
},
// 为每一张PDF页面创建canvas,并插入到缩略图区域和PDF预览区域
createCanvas({ page, container, pageNum, vw, isThum }) {
const viewport = vw || page.getViewport(this.scale);
const canvasEl = document.createElement('canvas');
const pageNumEl = document.createElement('div');
if (pageNum !== 1) {
canvasEl.style.display = 'none';
}
if (isThum) {
canvasEl.className = this.currentPage === pageNum ? this.thumItemActive : 'thumItem';
canvasEl.onclick = () => { this.goToPage(pageNum); };
canvasEl.style.display = 'inline-block';
pageNumEl.innerText = pageNum;
pageNumEl.style.color = '#ffffff';
}
container.appendChild(canvasEl);
if (isThum) {
container.appendChild(pageNumEl);
}
canvasEl.height = viewport.height;
canvasEl.width = viewport.width;
this.renderPDF({
page,
canvas: canvasEl,
viewport
});
this.loading.close();
},
// 处理拿到的pdf文件实例
handlePDF(total) {
// 对文件的每一页进行渲染
for (let pageNum = 1; pageNum <= total; pageNum++) {
this.pdfInstance.getPage(pageNum).then((page) => {
this.pdfContainer = document.getElementById('pdfContainer');
this.thumbnailContainer = document.getElementById('thumbnailContainer');
// 渲染PDF预览区域
this.createCanvas({
page,
container: this.pdfContainer,
isThum: false,
pageNum
});
// 渲染缩略图
this.createCanvas({
page,
container: this.thumbnailContainer,
pageNum,
isThum: true,
vw: page.getViewport(this.thumScale)
});
});
}
},
// 初始化加载blob格式PDF文件
init(url) {
const padUrlsd = url || this.pdfUrld;
const handlePDF = this.handlePDF;
PDF.getDocument(padUrlsd).then((pdf) => {
const total = this.pageTotalNum = pdf._pdfInfo.numPages;
this.pdfInstance = pdf;
handlePDF(total);
}).catch((error) => {
console.log(error);
this.$message.error('获取pdf文件失败');
this.loading.close();
});
}
}
};
</script>