备注:先仅记录pdf预览功能,翻页等功能日后整理。
两种pdf预览方式:
1、vue-pdf-embed 和 vue3-pdfjs 插件
2、pdfjs-dist 插件
一、vue-pdf-embed 和 vue3-pdfjs
1、安装插件
npm i vue-pdf-embed
npm i vue3-pdfjs
2、封装pdf预览组件
PDFView.vue 文件
<template>
<div
id="page-view"
:style="{
width: '100%',
height: `100%`
}"
>
<vue-pdf-embed :source="state.source" :page="state.pageNum" textLayer />
</div>
</template>
<script setup>
import { reactive, onMounted, ref } from 'vue';
import VuePdfEmbed from 'vue-pdf-embed';
import { createLoadingTask } from 'vue3-pdfjs';
const props = defineProps({
pdfUrl: {
type: String,
required: true
}
});
const state = reactive({
source: props.pdfUrl, //预览pdf文件地址
pageNum: 1, //当前页面
numPages: 0 // 总页数
});
onMounted(() => {
const loadingTask = createLoadingTask(state.source);
loadingTask.promise.then((pdf) => {
state.numPages = pdf.numPages;
});
});
</script>
3、在使用的地方调用组件
<template>
<div class="pdf-container">
<PDFView :pdfUrl="jsPdf" v-if="jsPdf && jsPdf != ''" />
<div v-else >加载中...</div>
</div>
</template>
<script setup>
import PDFView from './PDFView.vue';
import jsPdf from 'pdf文件地址';
</script>
如果pdf文件来源于后端返回的二进制流,可做如下处理(举例一种方法):
const jsPdf = ref('')
// ...接口调用,返回的二进制数据 data
const blob = window.URL.createObjectURL(new Blob([data], { type: 'application/pdf' }));
jsPdf.value = blob;
// 界面关闭时需要释放ObjectURL
window.URL.revokeObjectURL(jsPdf.value);
jsPdf.value = '';
备注:pdf预览界面样式自行调整。
二、pdfjs-dist
1、安装插件
npm i pdfjs-dist
2、使用方式一(直接引用)
<template>
<div class="pdf-container">
<canvas v-for="page in state.pdfPages" :key="page" id="pdfCanvas" />
<div id="text-view"></div>
</div>
</template>
<script setup>
import 'pdfjs-dist/web/pdf_viewer.css';
import * as PDF from 'pdfjs-dist';
import * as pdfjsWorker from 'pdfjs-dist/build/pdf.worker.mjs'; //必须
import jsPdf from 'pdf文件地址';
const state = reactive({
// 总页数
pdfPages: 1,
// 页面缩放
pdfScale: 2
});
let pdfDoc = null;
const loadFile = (url) => {
PDF.getDocument({
url,
cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.16.105/cmaps/',
cMapPacked: true
}).promise.then((pdf) => {
pdfDoc = pdf;
// 获取pdf文件总页数
state.pdfPages = pdf.numPages;
nextTick(() => {
renderPage(1); // 从第一页开始渲染
});
});
};
const renderPage = (num) => {
pdfDoc.getPage(num).then((page) => {
const canvas = document.getElementById('pdfCanvas');
const ctx = canvas.getContext('2d');
const viewport = page.getViewport({ scale: state.pdfScale });
canvas.width = viewport.width;
canvas.height = viewport.height;
const renderContext = {
canvasContext: ctx,
viewport
};
page.render(renderContext);
});
};
</script>
import * as pdfjsWorker from 'pdfjs-dist/build/pdf.worker.mjs'; 必须要引用,否则渲染时会报错:Error: No “GlobalWorkerOptions.workerSrc“ specified.
如果要一次性展示全部页面的话,可以将代码修改成:
<template>
<div id="pdf-container">
<canvas v-for="page in state.pdfPages" :key="page" :id="`page-${page}`" />
<div id="text-view"></div>
</div>
</template>
// 修改renderPage
<script setup>
const renderPage = (num)=> {
pdfDoc.getPage(num).then((page) => {
// 绑定 id 值对应的元素
const canvas = document.getElementById(`page-${num}`);
...
page.render(renderContext);
// state.pdfPages 为 pdf 文件总页数
if (num < state.pdfPages) {
renderPage(num + 1);
}
});
}
</script>
可能出现的问题:
部分字体出现乱码或浏览器控制台出现警告:

解决:在 getDocument 方法中追加 cMapUrl 和 cMapPacked 参数:
PDF.getDocument({
url,
cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.16.105/cmaps/',
cMapPacked: true,
})
注意:cMapUrl 参数可指定为本地文件路径,可在路径 node_modules/pdfjs-dist/cmaps 中获取。通过测试发现,该警告即便不处理依然不影响页面展示
3、使用方式二(封装组件)
创建 PDFBook.vue 文件
<template>
<div ref="flipRef" class="flip-book-wrapper">
<div class="container">
<div id="flipBook" class="flip-book"></div>
</div>
</div>
<div
class="pdf-loading"
v-loading="isLoading"
element-loading-text="文档加载中..."
v-if="isLoading"
></div>
</template>
<script setup>
import * as pdfjs from 'pdfjs-dist';
import 'pdfjs-dist/web/pdf_viewer.css';
import * as pdfjsWorker from 'pdfjs-dist/build/pdf.worker.mjs';
const props = defineProps({
url: {
type: String
},
width: {
type: Number
},
height: {
type: Number
},
page: {
type: Number
},
catalog: {
type: Array
}
});
const numPages = ref(0);
const scale = ref(1.0);
const pdfInit = async () => {
const pdfContainer = document.querySelector('.flip-book');
if (!pdfContainer) {
return;
}
const loadingTask = pdfjs.getDocument({
url: props.url
});
const pdf = await loadingTask.promise;
numPages.value = pdf.numPages;
for (let index = 0; index < numPages.value; index++) {
const page = await pdf.getPage(index + 1);
const viewport = page.getViewport({ scale: scale.value });
const canvas = document.createElement('canvas');
const PIXEL_RATIO = 3;
canvas.width = viewport.width * PIXEL_RATIO; // 实际渲染像素
canvas.height = viewport.height * PIXEL_RATIO; // 实际渲染像素
const context = canvas.getContext('2d');
if (!context) {
return;
}
const renderContext = {
canvasContext: context,
viewport: viewport,
transform: [
PIXEL_RATIO / window.devicePixelRatio,
0,
0,
PIXEL_RATIO / window.devicePixelRatio,
0,
0
]
};
await page.render(renderContext).promise;
pdfContainer.appendChild(canvas);
}
};
const isLoading = ref(false);
const init = async () => {
isLoading.value = true;
await pdfInit();
isLoading.value = false;
};
onBeforeUnmount(() => {});
onMounted(() => {
init();
});
</script>
调用组件
<template>
<PDFBook ref="pdfEBook" :url="jsPdf" v-if="jsPdf"></PDFBook>
</template>
<script setup>
import PDFBook from './PDFBook.vue';
import jsPdf from 'pdf文件地址';
</script>
1万+

被折叠的 条评论
为什么被折叠?



