实现样式
需求
实现PDF上传预览,并且不能下载
第一次实现:用vue-pdf,将上传的文件用base64传给前端展示
问题:
- 水印第一次加载有后面又没有了。
- 当上传大的pdf文件后,前端获取和渲染又长又慢,甚至不能用
修改实现模式
- 前端上传PDF,后端将PDF转化成一页一页的图片
- 前端根据page去获取一页一页的PDF图片,类似于百度文库
实现思路
配合后端实现思路
- 获取全部页数,先把侧边栏的元素画出来占个位置
- 获取已经看到的页数,没有默认1
- 渲染上次看到的页数,同时侧边栏滚动到相同的index位置,通过监听元素是否进入视口去获取base64图片
- 已经获取回来的图片不再去请求
主要难点是侧边栏懒加载和定位
<div class="pdf-viewer">
<div class="pdf-main">
<canvas id="pdf-view"></canvas>
</div>
<div class="pdf-list" :class="{ collapse: collapse }">
<div
class="pdf-item"
:class="{ active: currentPage === index }"
v-for="index in pageTotalNum"
:key="index"
@click="changePage(index)"
:data-index="index"
>
<img :src="imgList[index - 1]" alt="" />
</div>
</div>
</div>
<script>
let observer = null;
export default {
name: "PDFView",
data() {
return {
currentPage: 1, //当前页数
pageTotalNum: 1, //总页数
imgList: [], //base64图片列表
updateTimer: null
};
},
watch: {
/**
* @description 监听当前页变化 滚动列表到顶部
*/
currentPage() {
this.$nextTick(() => {
const activeEl = document.querySelector(".pdf-list .active");
if (activeEl) {
document.querySelector(".pdf-list").scrollTo({
top: activeEl.offsetTop - 20,
behavior: "smooth",
});
// 解决进来会请求当前页数 前面所有图片
setTimeout(() => {
if (observer) {
observer.disconnect();
}
this.isEnter();
}, 500);
}
});
},
},
mounted() {
this.getPageTotal();
},
beforeDestroy() {
if (observer) {
observer.disconnect();
}
},
methods: {
/**
* @description 获取pdf总页数
*/
getPageTotal() {
const params = {
id: this.$route.query.id,
};
apiGetViewPdfPageTotal(params).then((response) => {
this.pageTotalNum = response.data;
this.updateStudy(true);
});
},
/**
* @description 切换当前页
*/
changePage(index) {
this.currentPage = index;
this.updateStudy();
if (this.imgList[index - 1]) {
this.drawImage(this.imgList[index - 1]);
} else {
this.getPdf();
}
},
/**
* @description 上一页
*/
prePage() {
let page = this.currentPage;
if (page !== 1) {
page = page > 1 ? page - 1 : this.pageTotalNum;
this.currentPage = page;
this.updateStudy();
if (this.imgList[page - 1]) {
this.drawImage(this.imgList[page - 1]);
} else {
this.getPdf();
}
}
},
/**
* @description 下一页
*/
nextPage() {
let page = this.currentPage;
if (page !== this.pageTotalNum) {
page = page < this.pageTotalNum ? page + 1 : 1;
this.currentPage = page;
this.updateStudy();
if (this.imgList[page - 1]) {
this.drawImage(this.imgList[page - 1]);
} else {
this.getPdf();
}
}
},
/**
* @description 更新学习 flag=true第一次进入
*/
updateStudy(flag = false) {
const params = {
courseId: this.$route.query.id,
pageRate: this.currentPage,
flag,
totalPageRate: this.pageTotalNum,
};
apiUpdateStudy(params)
.then((response) => {
this.currentPage = response.data.pageRate;
if (flag) {
this.updateTimer = setInterval(() => {
this.updateStudy();
}, 1000 * 10);
}
if (flag) {
this.getPdf();
// 解决第一页进来不请求的问题,一页大概能展示4-5张
if (this.currentPage < 5) {
this.isEnter();
}
}
})
},
/**
* @description 查看资料
*/
getPdf() {
const params = {
id: this.$route.query.id,
page: this.currentPage,
};
apiGetPdf(params).then((response) => {
let base64 = "data:image/png;base64," + response.data;
this.drawImage(base64);
});
},
/**
* @description 将base64图片 画到canvas上
*/
drawImage(base64) {
const canvas = document.getElementById("pdf-view");
const context = canvas.getContext("2d");
const image = new Image();
image.src = base64;
image.onload = () => {
canvas.width = image.width;
canvas.height = image.height;
context.drawImage(image, 0, 0);
};
},
/**
* @description 监听元素进入视口
*/
isEnter() {
observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
const target = entry.target;
const index = target.dataset.index;
if (entry.isIntersecting) {
if (!this.imgList[index - 1]) {
this.getImgList(index);
}
} else {
// console.log("元素离开视口", index);
}
});
});
this.$nextTick(() => {
//将所有侧边栏的元素进行监听
const els = document.querySelectorAll(".pdf-item");
Array.from(els).forEach((el) => {
observer.observe(el);
});
});
},
/**
* @description 滚动获取图片
*/
getImgList(index) {
const params = {
id: this.$route.query.id,
page: index,
};
apiGetPdf(params).then((response) => {
let base64 = "data:image/png;base64," + response.data;
this.imgList[index - 1] = base64;
// 解决请求回来页面没更新的问题
this.$forceUpdate();
});
},
},
};
</script>