所用插件
pdfjs-dist,turn.js,jquery
由于turnjs需要用到jQuery,所以需要安装并配置jQuery,先配置jQuery,在vue.config.js文件中注入jQuery
config.plugin("provide").use(webpack.ProvidePlugin, [{
$: "jquery",
jquery: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
},]);
引入文件
主要需要这几个文件
import $ from 'jquery';
import '@/utils/turnjs4/lib/turn.js';
import * as pdfjs from 'pdfjs-dist';
pdfjs.GlobalWorkerOptions.workerSrc = `/pdf.worker.js`;
插件注意 turnjs自己去下载一个,放在utils文件夹里就行,然后就是pdf.worker.js这个文件,之前我在网上找的都是使用下面这种引入pdfjsWorker,但是我自己用着好像有点问题,我这里是自己下载的js文件放在public文件夹下面
import * as pdfjs from 'pdfjs-dist'
// 网上方法 不管用
import * as pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry'
pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker
个人下载的文件
逻辑代码实现
里面有些关于pdf大小的是因为我这里后台返回大pdf大小是不固定的所以需要进行调整
const getData = async () => {
try {
const result = await getPdf({ id: route.query.id });
await pdfInit(result);
await onTurn(); // 确保在 PDF 数据加载完成后执行翻页初始化
} catch (error) {
isSuccess.value = false;
console.error('请求出错', error);
}
};
onMounted(async () => {
await getData();
});
const pdfInit = async (url: string) => {
loading.value = true; // 设置加载状态为 true,显示加载动画
const pdfContainer = document.querySelector('#flipbook'); // 获取填充 PDF 的容器
if (!pdfContainer) {
return;
}
try {
// 使用 PDF.js 加载 PDF 文件
const loadingTask = pdfjs.getDocument({ url }); // 创建加载任务
const pdf = await loadingTask.promise; // 等待 PDF 加载完成
const container = document.querySelector('#flipbook'); // 获取 PDF 容器
// 获取第一页的宽高信息,用于调整显示比例
const demo = await pdf.getPage(1); // 获取第一页
viewSize.width = demo._pageInfo?.view[2]; // 获取第一页的宽度
viewSize.height = demo._pageInfo?.view[3]; // 获取第一页的高度
// 遍历 PDF 所有页面并渲染
for (let index = 0; index < pdf.numPages; index++) {
const page = await pdf.getPage(index + 1); // 获取当前页面
let viewport = null; // 定义视图端口
// 根据 PDF 页面的宽高比调整显示比例
if (viewSize.width < viewSize.height) {
if (page._pageInfo.view[2] > page._pageInfo.view[3]) {
viewport = page.getViewport({ scale: 0.8 }); // 纵向页面,横向较长,缩小显示
} else {
viewport = page.getViewport({ scale: 1 }); // 纵向页面,正常比例显示
}
} else {
viewport = page.getViewport({ scale: 1 }); // 横向页面,正常比例显示
}
// 创建 Canvas 元素用于渲染 PDF 页面
const canvas = document.createElement('canvas');
canvas.height = 1123; // 设置 Canvas 高度
canvas.width = viewport.width; // 设置 Canvas 宽度
const context = canvas.getContext('2d'); // 获取绘图上下文
if (!context) {
throw new Error('Cannot get canvas context'); // 如果无法获取上下文,抛出错误
}
// 定义渲染上下文
const renderContext = {
canvasContext: context,
viewport: viewport,
};
// 渲染 PDF 页面到 Canvas 上
await page.render(renderContext).promise;
// 将渲染好的 Canvas 包装到 DOM 结构中
const divPage = document.createElement('div'); // 页面容器
divPage.classList.add('page'); // 添加样式
const divPageContent = document.createElement('div'); // 页面内容容器
divPageContent.classList.add('page-content'); // 添加样式
canvas.className = 'canvas'; // 设置 Canvas 样式
divPageContent.appendChild(canvas); // 将 Canvas 添加到内容容器
divPage.appendChild(divPageContent); // 将内容容器添加到页面容器
container.appendChild(divPage); // 将页面容器添加到 PDF 容器
}
} catch (error) {
console.error('Error rendering PDF:', error); // 捕获并打印错误
} finally {
loading.value = false; // 加载完成,隐藏加载动画
}
};
const onTurn = () => {
$('#flipbook').turn({ // 初始化 Turn.js 翻页动画
autoCenter: true, // 自动居中
height: viewSize.height, // 设置高度
width: viewSize.height > viewSize.width ? viewSize.width * 2 : viewSize.width, // 设置宽度
display: viewSize.height > viewSize.width ? 'double' : 'single', // 单页或双页显示
elevation: 50, // 设置翻页的三维效果
duration: 500, // 翻页速度
gradients: true, // 翻页时的阴影渐变
acceleration: true, // 硬件加速
page: 1, // 初始显示页面
pages: pageCav.value.length, // 总页数
turnCorners: 'bl,br,tl,tr,l,r', // 设置可翻页的页角
when: { // 监听事件
turning: async function (e, page, view) { // 翻页中
console.log('e', e);
console.log('page', page);
console.log('view', view);
},
turned: function (e, page) { // 翻页完成
currentPage.value = page; // 更新当前页
},
last: function (e, page) { // 最后一页
message.info('最后一页');
},
},
});
};
完整代码
<template>
<div class="back" @click="back">
<a href="javascript:;">
<LeftOutlined style="font-size: 18px;" />
</a>
<input ref="selectFile" type="file" style="display: none;" />
</div>
<div v-if="isSuccess" class="box" v-show="!loading">
<div id="flipbook"></div>
<div class="prv">
<LeftOutlined @click="prev" class="change" />
<!-- <a-button type="primary" @click="prev">上一页</a-button> -->
</div>
<div class="next">
<RightOutlined @click="next" class="change" />
<!-- <a-button type="primary" @click="next">下一页</a-button> -->
</div>
</div>
<a-row justify="center" :gutter="40" style="margin-top: 30px" v-if="!loading">
<a-col v-if="!isSuccess">
<a-result status="404" title="" sub-title="页面加载失败">
<template #extra>
<a-button type="primary" @click="back">返回上一页</a-button>
</template>
</a-result>
</a-col>
</a-row>
<div class="example" v-show="loading">
<a-spin style="margin-top: 20%;" tip="数据加载中......." />
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, reactive, watch } from 'vue';
import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
import { useRouter, useRoute } from 'vue-router';
import { getPdf } from '@/api/dzgb/gbzs';
import { message } from 'ant-design-vue';
import $ from 'jquery';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import '@/utils/turnjs4/lib/turn.js';
import * as pdfjs from 'pdfjs-dist';
pdfjs.GlobalWorkerOptions.workerSrc = `/pdf.worker.js`;
// 判断请求是否成功
const isSuccess = ref(true);
const router = useRouter();
const loading = ref(true);
const route = useRoute();
const selectFile = ref(null);
// 存储pdf宽高
const viewSize = reactive({
width: null,
height: null,
});
// 页数
const pageCav = ref<any>([]);
const currentPage = ref(1);
const back = () => {
router.back();
};
const pdfInit = async (url) => {
loading.value = true;
const pdfContainer = document.querySelector('#flipbook');
if (!pdfContainer) {
return;
}
try {
// 使用 PDF.js 加载 PDF 数据
const loadingTask = pdfjs.getDocument({ url: `${url}` });
const pdf = await loadingTask.promise;
const container = document.querySelector('#flipbook');
// 获取pdf原件大小
const demo = await pdf.getPage(1);
viewSize.width = demo._pageInfo?.view[2];
viewSize.height = demo._pageInfo?.view[3];
for (let index = 0; index < pdf.numPages; index++) {
const page = await pdf.getPage(index + 1);
let viewport = null;
if (viewSize.width < viewSize.height) {
if (page._pageInfo.view[2] > page._pageInfo.view[3]) {
viewport = page.getViewport({ scale: 0.8 });
} else {
viewport = page.getViewport({ scale: 1 });
}
} else {
viewport = page.getViewport({ scale: 1 });
}
const canvas = document.createElement('canvas');
canvas.height = 1123;
canvas.width = viewport.width;
const context = canvas.getContext('2d');
if (!context) {
throw new Error('Cannot get canvas context');
}
const renderContext = {
canvasContext: context,
viewport: viewport,
};
await page.render(renderContext).promise;
const divPage = document.createElement('div');
divPage.classList.add('page');
const divPageContent = document.createElement('div');
divPageContent.classList.add('page-content');
canvas.className = 'canvas';
divPageContent.appendChild(canvas);
divPage.appendChild(divPageContent);
container.appendChild(divPage);
}
} catch (error) {
console.error('Error rendering PDF:', error);
} finally {
loading.value = false;
}
};
const getData = async () => {
try {
const result = await getPdf({ id: route.query.id });
await pdfInit(result);
await onTurn(); // 确保在 PDF 数据加载完成后执行翻页初始化
} catch (error) {
isSuccess.value = false;
console.error('请求出错', error);
}
};
onMounted(async () => {
await getData();
});
watch(() => route.query.id, () => {
console.log('111');
});
const onTurn = () => {
$('#flipbook').turn({
autoCenter: true, //自动居中, 默认false
height: viewSize.height, //高度
width: viewSize.height > viewSize.width ? viewSize.width * 2 : viewSize.width, //宽度
display: viewSize.height > viewSize.width ? 'double' : 'single', //单页显示/双页显示 single/double
elevation: 50,
duration: 500, //翻页速度(毫秒), 默认600ms
gradients: true, //翻页时的阴影渐变, 默认true
acceleration: true, //硬件加速, 默认true, 如果是触摸设备设置为true
page: 1, //设置当前显示第几页
pages: pageCav.value.length, //总页数
turnCorners: 'bl,br,tl,tr,l,r', // 设置可翻页的页角(都试过了,乱写 4个角都能出发卷起), bl,br tl,tr bl,tr
when: {
//监听事件
turning: async function (e, page, view) {
console.log('e', e);
console.log('page', page);
console.log('view', view);
},
turned: function (e, page) {
currentPage.value = page;
// 翻页后触发
},
last: function (e, page) {
message.info('最后一页');
},
},
});
};
// 翻页
const prev = () => {
$('#flipbook').turn('previous');
};
const next = () => {
$('#flipbook').turn('next');
};
</script>
<style scoped lang="less">
.box {
// width: 952px;
width: 100%;
overflow: hidden;
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.prv {
position: absolute;
top: 40%;
left: 0;
}
.next {
position: absolute;
top: 40%;
right: 0;
}
}
.page {
margin: 0 auto;
/* 居中对齐 */
}
.example {
text-align: center;
background: rgba(0, 0, 0, 0.05);
border-radius: 4px;
margin-bottom: 20px;
padding: 30px 50px;
height: 100%;
align-items: center;
justify-content: center;
margin: 20px 0;
}
.change {
font-size: 32px;
}
.back {
height: 30px;
width: 30px;
// background: red;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
float: left;
// position: fixed;
}
</style>