安装依赖
demo使用的vue为3.4.21,pdfjs-dist为4.3.136,pdf-lib为1.17.1。
npm i pdfjs-dist pdf-lib
创建vue组件
首先是html部分
<template>
<div class="pdf-wrapper">
<div ref="pdfContainer" class="pdf-container" @mousedown="onMouseDown">
<!-- PDF的canvas将会插入到这里 -->
</div>
<div class="controls">
<button @click="prevPage">上一页</button>
<button @click="nextPage">下一页</button>
<button @click="zoomIn">放大</button>
<button @click="zoomOut">缩小</button>
<button @click="downloadPDF">下载</button>
</div>
<div class="page-wrapper">
当前第{{ currentPage }}页,共{{ totalPages }}页
</div>
</div>
</template>
js部分
其中需要注意的部分是pdfjsLib.GlobalWorkerOptions.workerSrc会查到多种写法,大多数是引人的在线js,对于内网或者无互联网环境不友好,且因为版本问题无法直接替换引入的url中的版本。demo中使用的是安装的依赖中的js。(若版本不同导致报错,可评论探讨)
import { ref, onMounted } from "vue";
import * as pdfjsLib from "pdfjs-dist";
import * as workerSrc from "pdfjs-dist/build/pdf.worker.min?url";
import { PDFDocument } from "pdf-lib";
// demo的pdf的路径是从父组件传入在onMounted中进行赋值
const props = defineProps(["src"]);
// 配置 PDF.js worker 的路径
pdfjsLib.GlobalWorkerOptions.workerSrc = workerSrc.default;
const pdfContainer = ref(null); // pdf容器
const pdfUrl = ref(""); // 可替换为你的 PDF 文件路径
let pdfDoc = null; // pdf显示内容
let scale = ref(1.0); // 缩放比例
let currentPage = ref(1); // 当前页
let totalPages = ref(0); // 总页数
let isDragging = ref(false); // 是否可拖拽
let startX = ref(0); // 拖拽的x轴
let startY = ref(0); // 拖拽的y轴
let scrollLeft = ref(0); // 滚动的left
let scrollTop = ref(0); // 滚动的top
onMounted(async () => {
//对pdf的url进行赋值
pdfUrl.value = props.src;
//使用pdfjs-dist对pdf进行解析,该步骤为异步
const loadingTask = pdfjsLib.getDocument(pdfUrl.value);
pdfDoc = await loadingTask.promise;
//获取到总页数
totalPages.value = pdfDoc.numPages;
//使用渲染方法
renderPage(currentPage.value);
});
//渲染方法()
const renderPage = async (num) => {
const page = await pdfDoc.getPage(num);
const viewport = page.getViewport({ scale: scale.value });
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext = {
canvasContext: context,
viewport: viewport,
};
await page.render(renderContext).promise;
pdfContainer.value.innerHTML = "";
pdfContainer.value.appendChild(canvas);
};
//放大
const zoomIn = () => {
scale.value += 0.1;
renderPage(currentPage.value);
};
//缩小
const zoomOut = () => {
if (scale.value > 0.2) {
scale.value -= 0.1;
renderPage(currentPage.value);
}
};
//下一页
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value += 1;
renderPage(currentPage.value);
}
};
//上一页
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value -= 1;
renderPage(currentPage.value);
}
};
//下载
const downloadPDF = async () => {
const existingPdfBytes = await fetch(pdfUrl.value).then((res) =>
res.arrayBuffer()
);
const pdfDoc = await PDFDocument.load(existingPdfBytes);
const pdfBytes = await pdfDoc.save();
const blob = new Blob([pdfBytes], { type: "application/pdf" });
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = "downloaded.pdf";
link.click();
};
//用于实现拖拽
//鼠标按下
const onMouseDown = (e) => {
isDragging.value = true;
startX.value = e.pageX - pdfContainer.value.offsetLeft;
startY.value = e.pageY - pdfContainer.value.offsetTop;
scrollLeft.value = pdfContainer.value.scrollLeft;
scrollTop.value = pdfContainer.value.scrollTop;
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
};
//鼠标移动
const onMouseMove = (e) => {
if (!isDragging.value) return;
const x = e.pageX - pdfContainer.value.offsetLeft;
const y = e.pageY - pdfContainer.value.offsetTop;
const walkX = x - startX.value;
const walkY = y - startY.value;
pdfContainer.value.scrollLeft = scrollLeft.value - walkX;
pdfContainer.value.scrollTop = scrollTop.value - walkY;
};
//鼠标抬起
const onMouseUp = () => {
isDragging.value = false;
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
};
css部分
.pdf-wrapper {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
}
.pdf-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
overflow: auto;
cursor: grab;
}
.pdf-container:active {
cursor: grabbing;
}
.controls {
position: absolute;
top: 10px;
left: 10px;
display: flex;
justify-content: center;
gap: 10px;
margin-top: 10px;
}
button {
padding: 5px 10px;
cursor: pointer;
}
.page-wrapper {
font-size: 20px;
color: #fff;
}
demo地址
gitee地址:https://gitee.com/dasl1412/pdf-view
tips
更新一下小问题
[2024.6.28]
在实际使用时以上内容本地使用没有问题,但当部署在服务器之后(目前只是用了nginx进行部署),预览时会报错,报错如下:
这时候可以通过在nginx.conf内一下配置,可正常使用。(nginx -t检查配置时可能会报警告,目前暂未研究,nginx比较菜,如有大神可评论区指点)
types {
text/javascript js mjs;
}
[2024.7.9]
使用中发现了一下问题,由于篇幅问题,新写了一篇文章,若有使用该封装遇到问题的,可以移步查看是否是相似问题,若出现其他问题可以评论区讨论。(基于pdfjs-dist和pdf-lib实现PDF的预览(缩放、下载)(二))