使用插件:
版本定死了,别的都不行
"vue-pdf-embed": "^1.1.6",
"vue3-pdfjs": "^0.1.6",
npm引入
npm install vue-pdf-embed@1.1.6 vue3-pdfjs@0.1.6
我使用路由传参(pdf预览文件地址):
父组件:
const jump = (item) => {
// console.log(item.fileUrl);
router.push({ path: '/eBookPrv', query: { fileUrl: `/api/eBook/getFile/${item.fileUrl}` } });
}
预览组件:
只需要pdf预览文件地址,其他按我的代码来就好
<template>
<div class="container">
<img src="@/assets/svg/fh.svg" @click.prevent="backlist" class="back" />
<div class="pdf-view-list">
<div class="item active-item" v-for="(item, index) in state.numPages" :key="item.index" @click="itemindex(index)">
<div class="pdf-box">
<vue-pdf-embed :source="pdfUrl" class="vue-pdf-embed" :page="index + 1" />
</div>
<div class="page">{{ index + 1 }}</div>
<div class="mask" :class="{ active: state.numPages === index + 1 }"></div>
</div>
</div>
<div class="pdf-wrap" :ref="refs.wrapper" @wheel.prevent="scaleWheel($event)">
<div class="box" :ref="refs.box" @mousedown="dragstart($event)">
<vue-pdf-embed :source="pdfUrl" :style="scaleFun" class="vue-pdf-embed" :page="state.pageNum" />
</div>
<div class="zoomin-wrapper">
<img src="@/assets/svg/bif.svg" @click="rollBtn('enlarge')" />
<img src="@/assets/svg/small.svg" @click="rollBtn('zoomin')" alt="" />
<img src="@/assets/svg/fz.svg" @click="rolate" alt="" />
</div>
</div>
<div class="page-tool">
<div class="page-tool-item">{{ state.pageNum }}/{{ state.numPages }}</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed, watch } from "vue";
import VuePdfEmbed from "vue-pdf-embed/dist/vue3-pdf-embed";
import { createLoadingTask } from "vue3-pdfjs";
import { useRouter } from "vue-router";
import { useRoute } from "vue-router";
const router = useRouter();
const route = useRoute();
const pdfUrl = ref("");
const state = reactive({
pageNum: 1,
scale: 1,
numPages: 0,
});
onMounted(() => {
pdfUrl.value = route.query.fileUrl;
// console.log(route.query.fileUrl);
const loadingTask = createLoadingTask(pdfUrl.value);
loadingTask.promise.then((pdf) => {
state.numPages = pdf.numPages;
// console.log(pdf);
});
});
const itemindex = (i) => {
state.pageNum = i + 1;
};
const backlist = () => {
router.push("/eBook");
// console.log("返回列表");
};
// 实现pdf缩放
const scaleFun = computed(() => {
return `transform:scale(${scaleData.scale});transition: all 0.3s;`;
});
const refs = {
wrapper: ref(null), // pdf外层容器
box: ref(null), // pdf容器,用于拖拽
};
const dragData = reactive({
x: 0, // 拖拽初始化时的x坐标
y: 0, // 拖拽初始化时的y坐标
left: 0, // 拖拽结束时的x偏移量
top: 0, // 拖拽结束时的y偏移量
firstX: 0, // 初始x坐标
firstY: 0, // 初始y坐标
});
const scaleData = reactive({
scale: 1, // 缩放比例
scaleNum: 0.1, // 滚轮缩放比例
scaleMax: 100, // 最大缩放比例
scaleMin: 0.1, // 最小缩放比例
scaleBtn: 0.4, // 缩放按钮缩放比例
rotate: 0, // 旋转角度
});
watch(
() => state.numPages,
(v) => {
// 重置pdf大小和位置
scaleData.scale = 1;
scaleData.rotate = 0;
refs.box.value.style.left = '50%';
refs.box.value.style.top = '50%';
boxTransform();
},
);
// box 容器也要跟着变化
const boxTransform = () => {
refs.box.value.style.transform = `translate(-50%, -50%) rotate(${scaleData.rotate}deg) scale(${scaleData.scale})`;
};
// 旋转
const rolate = () => {
scaleData.rotate += 90;
boxTransform();
};
// 鼠标滚轮缩放
function scaleWheel(e) {
let dy = -e.deltaY || e.wheelDeltaY;
if (dy < 0) {
scaleData.scale -= scaleData.scaleNum;
} else {
scaleData.scale += scaleData.scaleNum;
}
// 边界判断
if (scaleData.scale >= scaleData.scaleMax) {
scaleData.scale = scaleData.scaleMax;
return;
}
if (scaleData.scale <= scaleData.scaleMin) {
scaleData.scale = scaleData.scaleMin;
return;
}
boxTransform();
return false;
}
// 点击放大缩小
const rollBtn = (action) => {
if (action === 'enlarge') {
scaleData.scale += scaleData.scaleBtn;
} else {
scaleData.scale -= scaleData.scaleBtn;
}
// 边界判断
if (scaleData.scale >= scaleData.scaleMax) {
scaleData.scale = scaleData.scaleMax;
return;
}
if (scaleData.scale <= scaleData.scaleMin) {
scaleData.scale = scaleData.scaleMin;
return;
}
boxTransform();
};
// 拖拽(box容器拖拽)
function dragstart(e) {
refs.box.value.style.transition = 'none';
e.preventDefault(); // 阻止默认事件
const box = refs.box.value;
const wrapper = refs.wrapper.value;
dragData.x = e.pageX - box.offsetLeft;
dragData.y = e.pageY - box.offsetTop;
// 添加鼠标移动事件
document.addEventListener('mousemove', move);
function move(event) {
// 计算元素的位置
dragData.left = event.pageX - dragData.x;
dragData.top = event.pageY - dragData.y;
// 边界判断可以在这里添加 ↓
// 设置元素的位置
box.style.left = dragData.left + 'px';
box.style.top = dragData.top + 'px';
}
// 添加鼠标抬起事件,鼠标抬起,将事件移除
document.addEventListener('mouseup', function () {
document.removeEventListener('mousemove', move);
});
// 鼠标离开父级元素,把事件移除
document.addEventListener('mouseout', function () {
document.removeEventListener('mousemove', move);
});
}
</script>
<style lang="scss" scoped>
.container {
background: #1f1f1f;
padding: 10px;
height: calc(100vh - 60px);
display: flex;
justify-content: center;
align-items: flex-start;
overflow-y: auto;
}
.pdf-preview {
position: relative;
height: 100vh;
padding: 20px 0;
box-sizing: border-box;
background-color: e9e9e9;
}
.pdf-wrap {
margin-left: 10px;
overflow-y: auto;
height: calc(100vh - 60px);
width: 40%;
position: relative;
overflow: hidden;
.box {
width: 80%;
height: 100%;
object-fit: contain;
user-select: none;
/* 不可选中,为了拖拽时不让文字高亮 */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
align-items: center;
justify-content: center;
.vue-pdf-embed {
width: 100%;
}
}
.zoomin-wrapper {
position: absolute;
top: 50%;
right: 20px;
transform: translateY(-50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
img {
width: 34px;
height: 34px;
cursor: pointer;
margin: 5px 0;
}
}
.center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.bottom-left {
position: absolute;
bottom: 20px;
left: 20px;
}
}
.vue-pdf-embed {
text-align: center;
border: 1px solid #e5e5e5;
margin: 0 auto;
box-sizing: border-box;
}
.vue-pdf-embed canvas {
width: 100% !important;
height: 100% !important;
}
.page-tool {
position: absolute;
bottom: 35px;
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%);
}
.page-tool-item {
padding: 8px 15px;
padding-left: 10px;
cursor: pointer;
}
.pdf-view-list {
width: 183px;
height: 100%;
background-color: #333333;
overflow-y: scroll;
// 隐藏滚动条
&::-webkit-scrollbar {
display: none;
}
.item {
width: 100%;
height: 257px;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 20px 20px 0 20px;
cursor: pointer;
.pdf-box {
width: 140px;
height: 203px;
background: #ffffff;
border-radius: 4px;
z-index: 1;
.vue-pdf-embed {
width: 100%;
height: 100%;
}
}
.page {
font-weight: 600;
font-size: 12px;
color: #ffffff;
line-height: 34px;
z-index: 1;
}
.mask {
width: 100%;
height: 100%;
background-color: transparent;
position: absolute;
left: 0;
top: 0;
z-index: 0;
}
.active {
background-color: #ffaa46;
opacity: 0.2;
}
}
.zoomin-wrapper {
position: absolute;
top: 50%;
right: 20px;
transform: translateY(-50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
img {
width: 34px;
height: 34px;
cursor: pointer;
margin: 5px 0;
}
}
}
.back {
position: absolute;
cursor: pointer;
top: 0;
left: 20px;
margin: 10px;
z-index: 100;
}
</style>
引用自: