是根据大佬的教程使用vue-pdf-embed+vue3-pdfjs做的预览pdf文件,但是现在已经找不到大佬的教程了,所以就不贴了。
本文主要记录一下使用这个方法预览pdf文件出现花屏的问题。
问题:
部分文件出现预览出现花屏失真的问题:
解决过程:
搜索问题得到答案:
解决使用 pdf.js 时出现花屏问题_vue pdfjs预览pdf花屏-CSDN博客
获得提示:版本问题
先来看一下几个包的版本:
查询npmjs得知vue-pdf-embed有更新
火速更新版本至最新
再次预览文件直接没了,显示不出来了,打印一下报错-----是vue-pdf-embed调用的api和pdfjs-dist的api版本不匹配。
经过排查是vue3-pdfjs中有一个pdfjs-dist包的版本很低,vue-pdf-embed引入了那个包,导致的版本不匹配无法预览,直接卸载vue3-pdfjs解决。
再来看一下版本:
这个时候预览就成功了,也不花屏了
但是vue3-pdfjs的包是用来获取总页数的,卸载后就无法获取总页数了。
使用vue-pdf-embed的loaded,看一下输出:
这个numPages就是总页数
function loaded(data) {
state.numPages = data._pdfInfo.numPages;
console.log(data);
}
这样就完美解决了问题。
最后放一下全部代码:
<!--
* @Author: 赵
* @Description: pdf预览
* @Date: 2024-08-14 13:43:49
* @LastEditTime: 2024-11-06 11:13:15
* @FilePath:
-->
<template>
<div class="pdf-preview">
<div class="pdf-wrap">
<vue-pdf-embed
:source="state.source"
:style="scale"
class="vue-pdf-embed"
:page="state.pageNum"
ref="PdfEmbedRef"
@loading-failed="loadingFild"
@loaded="loaded"
/>
</div>
<div class="bottom" v-if="props.controlBottom">
<div class="page-tool">
<div class="page-tool-item" @click="lastPage">上一页</div>
<div class="page-tool-item" @click="nextPage">下一页</div>
<div class="page-tool-item" style="display: flex;justify-content: center; align-items: center">
<!-- {{ state.pageNum }}/{{ state.numPages }} -->
<a-input-number
v-model:value="state.pageNum"
:min="1"
:max="state.numPages"
:controls="false"
size="small"
style="width: 40px;margin-right: 5px;"
/>
<div>
/{{state.numPages}}
</div>
</div>
<div class="page-tool-item" @click="pageZoomOut">放大</div>
<div class="page-tool-item" @click="pageZoomIn">缩小</div>
<div class="page-tool-item" @click="pageRest">重置缩放</div>
<div class="page-tool-item" @click="downloadPdf">下载</div>
<div class="page-tool-item" @click="onClose">关闭</div>
</div>
</div>
<div class="separate" v-if="!props.controlBottom">
<div class="page-tool page-tool-zoom">
<div class="page-tool-item" @click="pageZoomOut" style="font-size: 25px;">
<!-- 放大 -->
<ZoomInOutlined />
</div>
<div class="page-tool-item" @click="pageZoomIn" style="font-size: 25px;">
<!-- 缩小 -->
<ZoomOutOutlined />
</div>
</div>
<div class="page-tool page-tool-page">
<div class="page-tool-item" @click="lastPage" style="font-size: 25px;">
<!-- 上一页 -->
<LeftOutlined />
</div>
<div class="page-tool-item-page">
<a-input-number
v-model:value="state.pageNum"
:min="1"
:max="state.numPages"
:controls="false"
size="small"
style="width: 50px;margin-right: 5px;"
/>
<div>
/{{state.numPages}}
</div>
</div>
<div class="page-tool-item" @click="nextPage" style="font-size: 25px;">
<!-- 下一页 -->
<RightOutlined />
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, reactive, computed, defineEmits, watch } from "vue";
import VuePdfEmbed from "vue-pdf-embed";
import _ from "lodash";
const props = defineProps({
pdfUrl: {
type: String,
},
controlBottom: {
type: Boolean,
default: true,
}
})
const state = reactive({
source: {
url: props.pdfUrl,
// cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.9.359/cmaps/',
// cMapPacked: true
}, //预览pdf文件地址
pageNum: 1, //当前页面
scale: 1, // 缩放比例
numPages: 0, // 总页数
})
watch(
() => state.pageNum,
(value) => {
if(!value) { // 还没输入
state.pageNum = 1;
}
if(value > state.numPages) { //超出总页数
state.pageNum = state.numPages;
}
}
)
const scale = computed(() => `transform:scale(${state.scale})`)
function lastPage() { // 上一页
if (state.pageNum > 1) {
state.pageNum -= 1;
}
}
function nextPage() { // 下一页
if (state.pageNum < state.numPages) {
state.pageNum += 1;
}
}
function pageZoomOut() { // 放大
if (state.scale < 5) {
state.scale += 0.1;
}
}
function pageZoomIn() { // 缩小
if (state.scale > 0.3) {
state.scale -= 0.1;
}
}
// 页面 缩放 还原默认值
function pageRest() {
state.scale = 1.0;
}
// 下载功能
const PdfEmbedRef = ref();
function downloadPdf() {
PdfEmbedRef.value.download();
}
const emits = defineEmits("close")
// 关闭modal
function onClose() {
emits("close");
}
// --------------- 鼠标拖动-----------------------
let draggable;
let active = false;
let currentX;
let currentY;
let initialX;
let initialY;
let xOffset = 0;
let yOffset = 0;
function dragStart(e) {
initialX = e.clientX - xOffset;
initialY = e.clientY - yOffset;
console.dir(draggable);
if (draggable.className === "pdf-wrap") {
active = true;
}
}
function dragEnd(e) {
initialX = currentX;
initialY = currentY;
active = false;
}
function drag(e) {
if (active) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
xOffset = currentX;
yOffset = currentY;
setTranslate(currentX, currentY, draggable);
}
}
function setTranslate(xPos, yPos, el) {
el.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0)";
}
function loadingFild(data) {
console.log(data);
}
function loaded(data) {
state.numPages = data._pdfInfo.numPages;
console.log(data);
}
onMounted(() => {
// -------- 鼠标拖动 -------------
// new Draggable(document.querySelector(".pdf-wrap"));
draggable = document.querySelector(".pdf-wrap");
draggable.addEventListener('mousedown', dragStart, false);
document.addEventListener('mouseup', dragEnd, false);
document.addEventListener('mousemove', drag, false);
const pdfPreview = document.querySelector(".pdf-preview");
pdfPreview.addEventListener('mouseenter',() => {
window.addEventListener('wheel', wheel);
},false);
pdfPreview.addEventListener('mouseleave',() => {
window.removeEventListener('wheel',wheel);
})
})
function wheel(e) { // 放大缩小事件
// e.preventDefault();
if(e.deltaY < 0) { // 放大
pageZoomOut();
} else { // 缩小
pageZoomIn();
}
}
</script>
<style lang="less" scoped>
.pdf-preview {
position: relative;
height: 100%;
background-color: #ccc;
box-sizing: border-box;
overflow: hidden;
}
.vue-pdf-embed {
text-align: center;
width: 515px;
height: 100%;
border: 1px solid #e5e5e5;
margin: 0 auto;
box-sizing: border-box;
}
.bottom {
.page-tool {
position: absolute;
bottom: 8%;
padding-left: 15px;
padding-right: 15px;
display: flex;
align-items: center;
background: rgb(66, 66, 66);
color: white;
border-radius: 19px;
z-index: 100;
cursor: pointer;
margin-left: 50%;
transform: translateX(-50%);
user-select: none;
white-space: nowrap;
.page-tool-item {
padding: 8px 15px;
padding-left: 10px;
cursor: pointer;
}
}
}
.separate {
.page-tool {
position: fixed;
display: flex;
padding: 10px 0;
align-items: center;
background: rgb(66, 66, 66);
color: white;
border-radius: 19px;
cursor: pointer;
user-select: none;
}
.page-tool-zoom {
right: 20px;
top: 50%;
transform: translate(-50%,-50%);
flex-direction: column;
justify-content: center;
.page-tool-item {
padding: 8px 15px;
cursor: pointer;
}
}
.page-tool-page {
position: absolute;
left: 50%;
bottom: 2%;
transform: translateX(-50%);
.page-tool-item {
padding: 8px 15px;
cursor: pointer;
}
.page-tool-item-page {
display: flex;
align-items: center;
justify-content: center;
}
}
}
</style>
效果: