vue-pdf 解决安卓网页无法加载PDF的问题

155 阅读1分钟

效果

安卓模拟器上面的

录制.gif

原理

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" />