效果
安卓模拟器上面的
原理
css定位把一个轮播实例定位到PDF上面,通过轮播的切换事件控制PDF翻页
开始使用
需要的依赖
"swiper": "^4.5.1",
"@teckel/vue-pdf": "^4.3.5",
封装组件
pdf-viewer.vue
<!-- PDF查看器封装 -->
<template>
<div class="pdf-viewer-wrap">
<div class="pdf-viewer-swiper-wrap" :class="loading ? 'opacity-0' : ''">
<div class="swiper-container pdf-viewer-swiper">
<div class="swiper-wrapper">
<div v-for="item in totalPages" :key="item" class="swiper-slide"></div>
</div>
<!-- 上/下一页切换 -->
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
<!-- 页数指示器 -->
<span class="pdf-viewer-pagination">{{ `(${pageNum}/${totalPages})` }}</span>
</div>
<!-- PDF渲染 -->
<pdf
class="pdf-viewer-container"
:src="url"
:page="pageNum"
@page-loaded="pageLoaded"
@num-pages="(num) => (totalPages = num || 1)"
/>
</div>
</template>
<script>
import pdf from '@teckel/vue-pdf'
import 'swiper/dist/js/swiper'
import 'swiper/dist/css/swiper.css'
import Swiper from 'swiper'
export default {
components: { pdf },
props: [
// PDF文件的地址
'url',
],
data() {
return {
loading: true,
// PDF文件总页数
totalPages: 1,
// PDF文件当前的页码
pageNum: 1,
// 轮播组件实例对象
viewerSwiperCtx: undefined,
}
},
methods: {
/**
* 初始化轮播组件
*/
initViewerSwiperCtx() {
// 未初始化时才会进行初始化,防止被重复调用
if (this.viewerSwiperCtx === undefined) {
// 轮播的页是通过v-for渲染的,所以要在下一个循环再初始化,不然swiper会有异常
this.$nextTick(() => {
this.viewerSwiperCtx = new Swiper('.pdf-viewer-swiper', {
speed: 0,
grabCursor: true,
navigation: {
prevEl: '.pdf-viewer-swiper .swiper-button-prev',
nextEl: '.pdf-viewer-swiper .swiper-button-next',
},
on: {
/**
* 轮播开始切换的回调事件
*/
transitionStart: () => {
/** 翻页后的页码 */
const pageNum = this.viewerSwiperCtx.realIndex + 1
/**
* 翻页后的页码不等于当前页码时,才控制PDF翻页。
* 因为轮播未开启循环播放,切到首/末页时仍然可以触发transitionStart事件,所以这里得判断一下
*/
if (pageNum !== this.pageNum) {
this.pageNum = pageNum
}
},
},
})
})
}
},
/**
* 翻页加载完成的回调
* @param {Number} num 当前页码
*/
pageLoaded(num) {
this.pageNum = num
/**
* 因为vue-pdf这个库没有一个 整个文档加载完成的事件,
* 所以只能在每一次翻页加载完成后执行这些东西。
*/
this.loading = false
this.initViewerSwiperCtx()
},
},
}
</script>
<style lang="scss" scoped>
.pdf-viewer-wrap {
margin: 1rem auto;
max-width: 800px;
position: relative;
.pdf-viewer-swiper-wrap {
position: absolute;
z-index: 1;
top: 0;
left: 0;
height: 100%;
width: 100%;
.pdf-viewer-swiper {
height: 100%;
width: 100%;
// background-color: #f002;
.swiper-button-prev {
transform: scale(0.5);
filter: hue-rotate(145deg);
left: 0;
}
.swiper-button-next {
transform: scale(0.5);
filter: hue-rotate(145deg);
right: 0;
}
@media screen and(min-width: 768px) {
.swiper-button-prev {
transform: scale(0.8);
left: 1vw;
}
.swiper-button-next {
transform: scale(0.8);
right: 1vw;
}
}
}
.pdf-viewer-pagination {
position: absolute;
bottom: 8px;
right: 10px;
user-select: none;
color: #777;
}
}
.pdf-viewer-container {
border: 1px solid #e9e9e9;
}
}
</style>
使用组件
// 导入组件,略...
// 注册组件,略...
// template
<pdf-viewer :url="xxxxxxxxx.pdf" />