前言:
????????最近和小伙伴們一起合作完成一個企業(yè)級知識庫項目,其中一個功能就是后端把所有格式的文件轉(zhuǎn)換為PDF,前端實現(xiàn)渲染PDF文件從而實現(xiàn)預(yù)覽,干了整整一周(考慮到低版本瀏覽器的兼容),試用了幾種方案(iframe預(yù)覽已被廢棄,不適用本項目,想了解的同學(xué)自行搜索),終于實現(xiàn)了這個功能。
方案:Vue3+pdfjs-dist+canvas?
1.pdfjs-dist展示PDF文件的原理解釋
pdfjs-dist展示pdf文檔的原理,實際上是將pdf中的內(nèi)容渲染到解析,然后渲染到?canvas
?中進(jìn)行展示,因此我們使用pdfjs渲染出來的pdf文件,實際上是一張張canvas圖片。
2.安裝pdfjs-dist
pdfjs-dist下載官網(wǎng)
建議安裝:?"pdfjs-dist": "^2.12.313"
npm i pdfjs-dist@2.12.313
打開VScode,使用ctl+`打開控制臺,輸入:npm i pdfjs-dist 安裝pdfjs-dist 切記要指定版本
3.?搭建基礎(chǔ)的頁面代碼
話不多說,上代碼:文章來源:http://www.zghlxwxcb.cn/news/detail-806839.html
<template>
<div class="pdf-preview-box">
<!-- <div class="pdf_down">
<div class="pdf_set_left" @click="scaleD()">放大</div>
<div class="pdf_set_middle" @click="scaleX()">縮小</div>
<div class="pdf-pre" @click="prePage">上一頁</div>
<div class="pdf-next" @click="nextPage">下一頁</div>
</div> -->
<canvas class="pdf-viewer"
v-for="i in pdfParams.pdfPageTotal"
:key="i"
:id="'pdf-render' + i">
</canvas>
</div>
</template>
4.使用Vue3語法實現(xiàn)PDF文件的多頁展示
4.1引入ref
import { onMounted, ref, reactive } from 'vue'
4.2將會用到的數(shù)據(jù)聲明為響應(yīng)式
const props = defineProps({
pdfUrl: {
type: String,
default: '/testPdf.pdf',
required: true,
},
containerWidth: {
type: Number,
default: 700,
required: true,
},
});
const pdfParams = reactive({
currentPageNumber: 1,
pdfScale: 1.0,
pdfPageTotal: 0, // 總頁數(shù)
});
// 不要定義為ref或reactive格式,就定義為普通的變量
let pdfDoc = null;
4.3定義獲取pdf文檔流與pdf文件的頁數(shù)的方法:loadFile
// 這里必須使用異步去引用pdf文件,直接去import會報錯,也不知道為什么
const loadFile = async () => {
let pdfjs = await import('pdfjs-dist/build/pdf')
let pdfjsWorker = await import('pdfjs-dist/build/pdf.worker.entry')
pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker
// 此文件位于public/testPdf.pdf
pdfjs.getDocument(props.pdfUrl).promise.then(async doc => {
pdfDoc = doc
pdfParams.pdfPageTotal = doc.numPages
// // 僅加載第一頁 注釋 取消頁碼切換
// await getPdfPage(pdfParams.currentPageNumber)
// 加載pdf所有頁
for (let pageNum = 1; pageNum <= doc.numPages; pageNum++) {
await getPdfPage(pageNum)
}
})
}
4.4定義渲染pdf文件的方法:renderPage
// 加載pdf的某一頁
const getPdfPage = (number) => {
return new Promise((resolve, reject) => {
pdfDoc.getPage(number).then(page => {
const canvas = document.getElementById(`pdf-render${number}`)
const context = canvas.getContext('2d')
const scale = 1; // 縮放比例
const dpr = window.devicePixelRatio || 1;
const bsr =
context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio ||
1;
const ratio = dpr / bsr;
const viewport = page.getViewport({ scale: pdfParams.pdfScale }); // 獲取窗口大小
const canvasWidth = Math.floor(viewport.width * ratio);
const canvasHeight = Math.floor(viewport.height * ratio);
// const canvasWidth = props.containerWidth;
// const canvasHeight = Math.floor(viewport.height * ratio) * (props.containerWidth / Math.floor(viewport.width * ratio));
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// canvas.style.width = Math.floor(viewport.width) + 'px'
// canvas.style.height = Math.floor(viewport.height) + 'px'
canvas.style.width = Math.floor(props.containerWidth) + 'px'
canvas.style.height = Math.floor(viewport.height * props.containerWidth / viewport.width) + 'px'
let renderContext = {
canvasContext: context,
viewport: viewport,
// 這里transform的六個參數(shù),使用的是transform中的Matrix(矩陣)
// transform: [1, 0, 0, -1, 0, viewport.height]
transform: [ratio, 0, 0, ratio, 0, 0]
}
// 進(jìn)行渲染
page.render(renderContext).promise.then(() => {
resolve();
}).catch(error => {
reject(error);
});
}).catch(error => {
reject(error);
});
});
}
4.5在onMounted鉤子中調(diào)用loadFile方法
//調(diào)用loadFile方法
onMounted(async () => {
await loadFile()
})
5完整代碼實現(xiàn):
<!--
* @Author: 碼農(nóng)鍵盤上的夢
* @Description: pdf預(yù)覽插件 "pdfjs-dist": "^2.12.313", 指定版本 配合 canvas 實現(xiàn)一個組件
*
-->
<template>
<div class="pdf-preview-box">
<!-- <div class="pdf_down">
<div class="pdf_set_left" @click="scaleD()">?</div>
<div class="pdf_set_middle" @click="scaleX()">?</div>
<div class="pdf-pre" @click="prePage">上一頁</div>
<div class="pdf-next" @click="nextPage">下一頁</div>
</div> -->
<canvas class="pdf-viewer" v-for="i in pdfParams.pdfPageTotal" :key="i" :id="'pdf-render' + i"></canvas>
</div>
</template>
<script setup>
import { onMounted, ref, reactive } from 'vue'
const props = defineProps({
pdfUrl: {
type: String,
default: '/testPdf.pdf',
required: true,
},
containerWidth: {
type: Number,
default: 700,
required: true,
},
});
const pdfParams = reactive({
currentPageNumber: 1,
pdfScale: 1.0,
pdfPageTotal: 0, // 總頁數(shù)
});
// 不要定義為ref或reactive格式,就定義為普通的變量
let pdfDoc = null;
onMounted(async () => {
await loadFile()
})
// 這里必須使用異步去引用pdf文件,直接去import會報錯,也不知道為什么
const loadFile = async () => {
let pdfjs = await import('pdfjs-dist/build/pdf')
let pdfjsWorker = await import('pdfjs-dist/build/pdf.worker.entry')
pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker
// 此文件位于public/testPdf.pdf
pdfjs.getDocument(props.pdfUrl).promise.then(async doc => {
pdfDoc = doc
pdfParams.pdfPageTotal = doc.numPages
// // 僅加載第一頁 注釋 取消頁碼切換
// await getPdfPage(pdfParams.currentPageNumber)
// 加載pdf所有頁
for (let pageNum = 1; pageNum <= doc.numPages; pageNum++) {
await getPdfPage(pageNum)
}
})
}
// 加載pdf的某一頁
const getPdfPage = (number) => {
return new Promise((resolve, reject) => {
pdfDoc.getPage(number).then(page => {
const canvas = document.getElementById(`pdf-render${number}`)
const context = canvas.getContext('2d')
const scale = 1; // 縮放比例
const dpr = window.devicePixelRatio || 1;
const bsr =
context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio ||
1;
const ratio = dpr / bsr;
const viewport = page.getViewport({ scale: pdfParams.pdfScale }); // 獲取窗口大小
const canvasWidth = Math.floor(viewport.width * ratio);
const canvasHeight = Math.floor(viewport.height * ratio);
// const canvasWidth = props.containerWidth;
// const canvasHeight = Math.floor(viewport.height * ratio) * (props.containerWidth / Math.floor(viewport.width * ratio));
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// canvas.style.width = Math.floor(viewport.width) + 'px'
// canvas.style.height = Math.floor(viewport.height) + 'px'
canvas.style.width = Math.floor(props.containerWidth) + 'px'
canvas.style.height = Math.floor(viewport.height * props.containerWidth / viewport.width) + 'px'
let renderContext = {
canvasContext: context,
viewport: viewport,
// 這里transform的六個參數(shù),使用的是transform中的Matrix(矩陣)
// transform: [1, 0, 0, -1, 0, viewport.height]
transform: [ratio, 0, 0, ratio, 0, 0]
}
// 進(jìn)行渲染
page.render(renderContext).promise.then(() => {
resolve();
}).catch(error => {
reject(error);
});
}).catch(error => {
reject(error);
});
});
}
// // 下一頁功能
// const prevPage = () => {
// if (pdfParams.currentPageNumber > 1) {
// pdfParams.currentPageNumber -= 1
// } else {
// pdfParams.currentPageNumber = 1
// }
// getPdfPage(pdfParams.currentPageNumber)
// }
// // 上一頁功能
// const nextPage = () => {
// if (pdfParams.currentPageNumber < pdfParams.pdfPageTotal) {
// pdfParams.currentPageNumber += 1
// } else {
// pdfParams.currentPageNumber = pdfParams.pdfPageTotal
// }
// getPdfPage(pdfParams.currentPageNumber)
// }
// //放大
// const scaleD = async () => {
// let max = 0;
// if (window.screen.width > 1440) {
// max = 1.4;
// } else {
// max = 1.2;
// }
// if (pdfParams.pdfScale >= max) {
// return;
// }
// pdfParams.pdfScale = pdfParams.pdfScale + 0.1;
// await loadFile();
// }
// //縮小
// const scaleX = async () => {
// let min = 1.0;
// if (pdfParams.pdfScale <= min) {
// return;
// }
// pdfParams.pdfScale = pdfParams.pdfScale - 0.1;
// await loadFile();
// }
</script>
<style scoped lang="scss" >
.pdf-preview-box {
width: 100%;
position: relative;
// .pdf_down {
// position: fixed;
// display: flex;
// z-index: 20;
// right: 26px;
// bottom: 7%;
// cursor: pointer;
// .pdf_set_left {
// width: 30px;
// height: 40px;
// color: #408fff;
// font-size: 15px;
// padding-top: 25px;
// text-align: center;
// margin-right: 5px;
// cursor: pointer;
// }
// .pdf_set_middle {
// width: 30px;
// height: 40px;
// color: #408fff;
// font-size: 15px;
// padding-top: 25px;
// text-align: center;
// margin-right: 5px;
// cursor: pointer;
// }
// .pdf-pre {
// position: fixed;
// display: flex;
// z-index: 20;
// right: 160px;
// bottom: 9%;
// cursor: pointer;
// }
// .pdf-next {
// position: fixed;
// display: flex;
// z-index: 20;
// right: 100px;
// bottom: 9%;
// }
// }
}
</style>
以上就是實現(xiàn)PDF文件多頁展示的內(nèi)容了,如果其他的小伙伴有其他的方法或者思考請批評指正?文章來源地址http://www.zghlxwxcb.cn/news/detail-806839.html
到了這里,關(guān)于Vue3實現(xiàn)PDF文件預(yù)覽 (低版本瀏覽器兼容)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!