当前基于
vite 5.3.1、vue 3.4.29、pdfjs-dist 5.3.31
实现效果
效果一:不分页,下滑查看完整pdf
效果二: 分页,点击切换页面
具体实现
使用npm引入pdfjs-dist
npm install pdfjs-dist
代码实现
<script setup>
import {onBeforeRouteUpdate, useRoute} from 'vue-router'
//获取 pdf 地址
import {getWikiPageDetail} from "@/api/modules/api.wiki.page.js";
import { onMounted, reactive } from 'vue';
//在引入的时候需要考虑打包方式是vite
import * as pdfjs from 'pdfjs-dist';
pdfjs.GlobalWorkerOptions.workerSrc = new URL("pdfjs-dist/build/pdf.worker.min.mjs",import.meta.url).toString();
const pdfParams = reactive({
showPage: 0, //是否分页显示 1 是 0 否
pageNumber: 1, // 当前页
total: 0, // 总页数
scale: 1, // 缩放
rotate: 0 // 旋转角度
});
// 不要定义为ref或reactive格式,就定义为普通的变量
let pdfDoc = null;
let currentRoute = useRoute();
onBeforeRouteUpdate((to) => {
//点击route更新的时候加载pdf
loadPageDetail(to.params.pageId)
});
onMounted(async() => {
//初始进入加载pdf
loadPageDetail(currentRoute.params.pageId)
});
const loadPageDetail = async (pageId) =>{
// 加载pdf文件,本地文件会有跨域问题,下面这个地址是不对的,自行更换
const result = (await getWikiPageDetail(pageId)).data
const url = result.pageContent.content;
pdfjs.getDocument(url).promise.then(doc => {
pdfDoc = doc;
pdfParams.total = doc.numPages;
getPdfPage(1);
});
}
// 加载pdf的某一页
const getPdfPage = (number) => {
if(pdfParams.total < number){
return
}
pdfDoc.getPage(number).then((page) => {
// 获取视图,并设置缩放
const viewport = page.getViewport(
{
scale: pdfParams.scale, // 缩放
rotation: pdfParams.rotate // 旋转
});
const outputScale = window.devicePixelRatio || 1;
// 获取canvas
//分页返回: 'pdf-render' 不分页返回: 'pdf-render-num'
const canvas = document.getElementById('pdf-render' + ((pdfParams.showPage == 1)?'':'-'+number));
const context = canvas.getContext('2d');
// 设置canvas的宽高
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);
canvas.style.width = Math.floor(viewport.width) + 'px';
canvas.style.height = Math.floor(viewport.height) + 'px';
var transform = outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
// 渲染pdf
var renderContext = {
canvasContext: context,
transform: transform,
viewport: viewport
};
page.render(renderContext);
//不分页的情况下,自动加载下一页
if(pdfParams.showPage == 0){
getPdfPage(number + 1)
}
});
};
// 前一页
const prevPage = () => {
if (pdfParams.pageNumber > 1) {
pdfParams.pageNumber -= 1;
} else {
pdfParams.pageNumber = 1;
}
// 重新渲染
getPdfPage(pdfParams.pageNumber);
};
// 下一页
const nextPage = () => {
if (pdfParams.pageNumber < pdfParams.total) {
pdfParams.pageNumber += 1;
} else {
pdfParams.pageNumber = pdfParams.total;
}
// 重新渲染
getPdfPage(pdfParams.pageNumber);
};
// 旋转
const rotatePage = () => {
pdfParams.rotate += 90;
// 重新渲染
getPdfPage(pdfParams.pageNumber);
};
// 放大
const toBig = () => {
if (pdfParams.scale < 5) {
pdfParams.scale += 0.5;
} else {
pdfParams.scale = 5;
}
// 重新渲染
getPdfPage(pdfParams.pageNumber);
};
// 缩小
const toSmall = () => {
if (pdfParams.scale > 1) {
pdfParams.scale -= 0.5;
} else {
pdfParams.scale = 1;
}
// 重新渲染
getPdfPage(pdfParams.pageNumber);
};
</script>
<template>
<div class="main-container">
<div class="pdf-show-vue">
<div class="tool-bar">
<div v-if="pdfParams.showPage == 1">{{ pdfParams.pageNumber }} / {{ pdfParams.total }}</div>
<div v-else>{{ pdfParams.total }}</div>
<div>
<el-button v-if="pdfParams.showPage == 1" type="primary" :disabled="pdfParams.pageNumber == pdfParams.total" @click="nextPage">下一页
</el-button>
<el-button v-if="pdfParams.showPage == 1" type="primary" :disabled="pdfParams.pageNumber == 1" @click="prevPage">上一页</el-button>
<el-button type="primary" @click="rotatePage">旋转</el-button>
<el-button type="primary" :disabled="pdfParams.scale==5" @click="toBig">放大</el-button>
<el-button type="primary" :disabled="pdfParams.scale==1" @click="toSmall">缩小</el-button>
</div>
</div>
<!-- 不分页 -->
<div v-if="pdfParams.showPage == 0">
<canvas v-for="page in pdfParams.total" :id="'pdf-render-'+page" :key="page" ></canvas>
</div>
<!-- 分页 -->
<div v-else>
<canvas id="pdf-render"></canvas>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.main-container {
display: flex;
flex-direction: column;
transition: margin-left 0.28s;
}
.pdf-show-vue {
position: relative;
overflow: hidden;
box-sizing: border-box;
height: calc(100vh - $navbar-height);
overflow-y: scroll; /* 确保y轴有滚动 */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* Internet Explorer 10+ */
padding-top: 10px;
.tool-bar{
display: flex;
justify-content: space-between;
margin-inline: 20px;
position: sticky;
top: 0;
transition: width .28s;
}
}
canvas{
display: block; /* 一个canvas占一行 */
margin: 0 auto; /* 居中 */
}
</style>
代码说明一:引入pdfjs-dist
- 使用
npm install pdfjs-dist引入pdfjs-dist - 加载
pdf.worker.min.mjs因为是需要String的类型,所以写法和官网上的案例不太一致
import * as pdfjs from 'pdfjs-dist';
pdfjs.GlobalWorkerOptions.workerSrc = new URL("pdfjs-dist/build/pdf.worker.min.mjs",import.meta.url).toString();
代码说明二:tool-bar的css样式
- 悬浮,不因滑动隐藏
position: sticky;
top: 0;
transition: width .28s;
- 两个子div平均分布
display: flex;
justify-content: space-between;
部署后,使用nginx代理报错
报错内容:Expected a JavaScript module script but the server responded with a MIME type of "application/octet-stream". Strict MIME type checking is enforced for module scripts per HTML spec.
处理
server {
include mime.types;
types {
application/javascript mjs js;
application/pdf pdf;
}
}