實現(xiàn)樣式
需求
實現(xiàn)PDF上傳預覽,并且不能下載
第一次實現(xiàn):用vue-pdf,將上傳的文件用base64傳給前端展示
問題:
- 水印第一次加載有后面又沒有了。
- 當上傳大的pdf文件后,前端獲取和渲染又長又慢,甚至不能用
修改實現(xiàn)模式
- 前端上傳PDF,后端將PDF轉化成一頁一頁的圖片
- 前端根據page去獲取一頁一頁的PDF圖片,類似于百度文庫
實現(xiàn)思路
配合后端實現(xiàn)思路文章來源:http://www.zghlxwxcb.cn/news/detail-821538.html
- 獲取全部頁數,先把側邊欄的元素畫出來占個位置
- 獲取已經看到的頁數,沒有默認1
- 渲染上次看到的頁數,同時側邊欄滾動到相同的index位置,通過監(jiān)聽元素是否進入視口去獲取base64圖片
- 已經獲取回來的圖片不再去請求
主要重點難點是側邊欄懶加載、定位、等比例展示圖片文章來源地址http://www.zghlxwxcb.cn/news/detail-821538.html
<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 監(jiān)聽當前頁變化 滾動列表到頂部
*/
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);
}
// 切換頁面 將查看區(qū)域滾動到最上面
const mainEl = document.querySelector(".pdf-main");
mainEl.scrollTo({
top: 0,
});
});
},
},
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 = () => {
const proportion = image.width / image.height;
// 獲取style設置width:100% 的canvas寬度
const canvasWidth = canvas.offsetWidth;
// 圖片寬度與canvas寬度比例
const canvasWidthProportion = image.width / canvasWidth;
// canvas寬度設置為寬度
canvas.width = image.width;
// 根據圖片比例和寬度比例計算出canvas高度
canvas.height = (canvasWidth / proportion) * canvasWidthProportion;
context.drawImage(image, 0, 0);
};
},
/**
* @description 監(jiān)聽元素進入視口
*/
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(() => {
//將所有側邊欄的元素進行監(jiān)聽
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>
<style lang="scss" scoped>
.pdf-container {
width: 100%;
height: 100%;
color: #999;
}
.pdf-viewer {
width: 100%;
height: calc(100vh - 50px - 30px - 60px - 6px);
position: relative;
display: flex;
}
.pdf-list {
width: 240px;
overflow-y: auto;
display: flex;
flex-direction: column;
padding: 20px;
background: #000;
box-sizing: border-box;
// transition: all 0.3s ease-in-out;
border-left: 1px solid #999;
&::-webkit-scrollbar {
width: 0px;
}
.pdf-item {
height: 183px;
min-height: 183px;
display: inline-flex;
justify-content: center;
align-items: center;
cursor: pointer;
overflow: hidden;
&:hover {
::v-deep img {
transition: all 0.5s ease-in-out;
transform: scale(1.1);
}
}
&.active {
box-shadow: 0px 0px 0px 4px #e6a23c;
}
&:not(:last-child) {
margin-bottom: 10px;
}
img {
pointer-events: none;
width: 100%;
// height: 100%;
}
}
&.collapse {
width: 0;
padding: 0;
}
}
.pdf-main {
flex: 1;
// width: 100%;
// height: 100%;
overflow-y: auto;
background: #000;
position: relative;
padding: 10px 0;
&::-webkit-scrollbar {
width: 0px;
}
}
.handle-btn {
background: #000;
display: flex;
font-size: 12px;
position: relative;
height: 60px;
padding: 0 6px;
border-bottom: 1px solid #999;
.right {
width: 240px;
display: flex;
align-items: center;
justify-content: flex-end;
font-size: 32px;
}
.main {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 32px;
margin-left: 250px;
.pagination {
display: flex;
align-items: center;
margin: 0 10px;
.pagination-info {
font-size: 14px;
margin: 0 8px;
}
}
.zoom {
display: flex;
align-items: center;
margin: 0 10px;
.scale {
font-size: 14px;
margin: 0 8px;
}
}
}
.tips {
color: #e6a23c;
font-size: 12px;
}
.start-test {
display: flex;
align-items: center;
}
.time {
position: absolute;
left: 6px;
top: 50%;
transform: translateY(-50%);
> span {
display: inline-block;
margin-left: 10px;
}
}
}
i {
cursor: pointer;
&:hover {
color: #fff;
}
}
#pdf-view {
width: 100%;
// height: 100%;
padding: 10px;
}
</style>
到了這里,關于Vue實現(xiàn)圖片預覽,側邊欄懶加載,不用任何插件,簡單好用的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!