nuxt,vue中使用pdfjs实现滚动加载、页码跳转、放大缩小

产品需求: pdf是分章节上传到服务器上的, 我拿到的是url数组对象, 所以要实现根据具体页码 请求不同的url地址效

效果图: 支持滚动加载, 使用elementui 的分页实现页码跳转, 支持放大缩小

image.png

1.我们使用是 nuxt开发, 首先安装pdfjs-dist

2. plugins中引入

image.png

3.组件中引入

import pdfjsLib from '~/plugins/pdf';

4. 核心代码, 后端放回的是一个url数组对象, 需要根据页码计算出请求哪个地址, 在数组中插入的位置

   _loadFile(url, index = 0, offsetPage = 0, offsetIndex = 0, needJump = false) {
            if (this.isRenderring) {
                return;
            }
            if (index === this.lastIndex) {
                return;
            };
            this.lastIndex = index;
            if (this.pdfData.some(item => item.index == index)) {
                return;
            }
            if (index < 0 || !url || offsetPage >= this.totalPage) {
                return;
            }
            // console.log('_loadFile执行===>', { url, index, offsetPage, offsetIndex, needJump });
            this.isRenderring = true;
  pdfjsLib.getDocument({ url }).promise.then((pdf) => {
                    this.pdfDoc = pdf;
                    const pages = pdf.numPages;
                    // 计算该插入的位置
                    let idx = 0;
                    this.pdfData.forEach((it, num) => {
                        if (it.index < index) {
                            idx = num + 1; // 插入的是下一个位置
                        } else {
                            return;
                        }
                    });
                    // 当前的url 能分几组
                    const count = Math.ceil(pages / this.size);
                    const start = 1 + (index - offsetIndex) * this.size + offsetPage;
                    const end = index - offsetIndex < count - 1 ? (index - offsetIndex + 1) * this.size + offsetPage : pages + offsetPage;
                    const pageList = this.range(start, end);

                    // 插入一组连续的页码
                    this.pdfData.splice(idx, 0, {
                        url,
                        index, // 第几组 从零开始
                        offsetIndex, // 前面的url分了几组
                        offsetPage, // 前面的url 一共到第几页
                        idx, // 该分组在数组中的序号
                        count, // 当前url能分几组
                        pages, // 当前url的页码数

                        prev: index - offsetIndex === 0 ? null : index - 1,
                        next: index - offsetIndex === count - 1 ? null : index + 1,
                        pageList,
                        length: pageList.length,
                        index: index,
                        start, //
                        end // 当前分组的页码范围
                    });

                    if (this.pdfData.length > 6) {
                        this.pdfData = this.pdfData.slice(idx < 3 ? 0 : idx - 3, idx + 3);
                    }
                    // 等canvas挂载到dom上后,
                    this.$nextTick(() => {
                        pageList.forEach(async (it, num) => {
                            await this._renderPage(it, offsetPage);
                            if (num === pageList.length - 1) {
                                this.isRenderring = false;
                                if (needJump) {
                                    document.getElementById('the-canvas' + this.currentPage).scrollIntoView();
                                }
                            }
                        });
                    });
                });
}

// 渲染指定页到对应的canvas上面

        _renderPage(num, offsetPage = 0) {
            try {
                return this.pdfDoc.getPage(num - offsetPage).then((page) => {
                    let canvas = document.getElementById(`the-canvas${num}`);
                    let ctx = canvas.getContext('2d');
                    let viewport = page.getViewport({ scale: document.querySelector('.canvas-cotainner').offsetWidth / page.getViewport({ scale: 1 }).width });
                    canvas.width = viewport.width;
                    canvas.height = viewport.height;
                    this.pageHeight = parseInt(viewport.height + 10);
                    let renderContext = {
                        canvasContext: ctx,
                        viewport: viewport
                    };
                    return page.render(renderContext);
                });
            } catch (err) {
                console.log('_renderPage===>', err);
                this.isRenderring = false;
            }
        },

// 模版

        <div
            class="canvas-cotainner"
            v-scroll="loadMore"
        >
            <template v-for="item in pdfData">
                <canvas
                    v-for="page in item.pageList"
                    :data-prev="item.prev"
                    :data-next="item.next"
                    :data-index="item.index"
                    :data-start="item.start"
                    :data-end="item.end"
                    :data-page="page"
                    :id="'the-canvas'+page"
                    :key="`${page}${resize}`"
                ></canvas>
            </template>
        </div>

5.使用自定义指令,在容器盒子添加滚动事件

   directives: {
        scroll: {
            bind(el, binding, vnode) {
                let beforeScrollTop = 0;
                let timer;
                el.addEventListener('scroll', function (e) {
                    if (timer) {
                        clearTimeout(timer);
                    }
                    if (Math.abs(this.scrollTop - beforeScrollTop) > 300) {
                        beforeScrollTop = this.scrollTop;
                        return;
                    }
                    timer = setTimeout(() => {
                    if (vnode.context.isRenderring) {
                        return;
                    }
                    if (this.scrollTop === beforeScrollTop) {
                        return;
                    }
                    // 判断滚动方向
                    const beforeTopDistance = beforeScrollTop;
                    const direction = this.scrollTop - beforeScrollTop > 0 ? 'down' : 'up';
                    beforeScrollTop = this.scrollTop;
                    // 计算出容器视图内  当前显示的哪个canvas
                    const idx = Math.floor(this.scrollTop / (vnode.context.pageHeight));
                    const scrollDistance = this.scrollHeight - this.scrollTop - this.clientHeight;
                    if (this.children[idx]) {
                        vnode.context.currentPage = parseInt(this.children[idx].dataset.page);
                        binding.value(direction, this.children[idx]);
                    }
                    }, 20);
                }, { capture: false, passive: false, once: false });
            }
        }

    }

// 滚动加载函数
// 每次加载都去加载下一组, 每组为10页,
// 当单前链接url读完的时候, 尝试去计算有没有下一个url
loadMore(dir, dom) {
if (dir === 'down') {
// 当前链接读完
if (dom.dataset.next === undefined) {
// 期望去读下一组的第一页
this.calcUrlByPage(dom.dataset.end - 0 + 1);
if (this.currUrl) {
this.offsetIndex = dom.dataset.index - 0 + 1;
this.offsetPage = dom.dataset.end - 0;
this._loadFile(this.currUrl, dom.dataset.index - 0 + 1, dom.dataset.end - 0, dom.dataset.index - 0 + 1);
}
} else if (!this.isRenderring) {
const i = this.pdfData.findIndex(item => item.index == dom.dataset.index);
if (!this.pdfData[i + 1] || this.pdfData[i + 1].index != dom.dataset.next) {
if (dir !== this.dir) {
this.calcUrlByPage(dom.dataset.end - 0 + 1);
}
if (this.currUrl) {
this._loadFile(this.currUrl, dom.dataset.next - 0, this.offsetPage, this.offsetIndex);
}
}
}
}
// && dom.dataset.page < dom.dataset.start + 3
if (dir === 'up') {
if (dom.dataset.prev === undefined) {
this.calcUrlByPage(dom.dataset.start - 1);

                if (this.currUrl) {
                    this.offsetIndex = dom.dataset.index - 1;
                    this._loadFile(this.currUrl, dom.dataset.index - 1, this.offsetPage, this.offsetIndex);
                }
            } else if (!this.isRenderring) {
                const i = this.pdfData.findIndex(item => item.index == dom.dataset.index);
                if (!this.pdfData[i - 1] || this.pdfData[i - 1].index != dom.dataset.prev) {
                    if (dir !== this.dir) {
                        this.calcUrlByPage(dom.dataset.start - 1);
                    }
                    if (this.currUrl !== '') {
                        this._loadFile(this.currUrl, dom.dataset.prev - 0, this.offsetPage, this.offsetIndex);
                    }
                }
            }
        }
        this.dir = dir;
    }

### 6.pdfData的数据格式, 
![image.png](https://upload-images.jianshu.io/upload_images/17802834-aca2bdf53d61b7a5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

### 7. 问题补充
  #### 跨域,
    跨域需要后端配置 nginx, 在响应头中加'Access-Control-Allow-Origin' '*'

'Access-Control-Allow-Origin' '*'


### pdf.worker.min.js 或者pdf.worker.js 找不到 请求这个资源报404怎么办?
 *** 直接把pdf.worker.min.js 复制到静态资源中去引用, 如图
![image.png](https://upload-images.jianshu.io/upload_images/17802834-5a0f903c8a6ebcc6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

### 8 跳到指定页码
    <el-pagination
        @size-change="sizeChange"
        @current-change="currentChange"
        :current-page.sync="currentPage"
        :page-size="1"
        layout="prev, pager, next, jumper"
        :total="totalPage"
        class="pagebar"
    ></el-pagination>
    currentChange(val) {
        // if (this.isRenderring) {
        //     return;
        // }
        this.isRenderring = false;
        if (val > 0) {
            this.currentPage = val;
        }
        try {
            if (!document.getElementById('the-canvas' + val)) {
                this.calcUrlByPage(val);

                const index = Math.floor((val - 1 - this.offsetPage) / this.size);
                if (this.currUrl) {
                    this._loadFile(this.currUrl, index + this.offsetIndex, this.offsetPage, this.offsetIndex, true);
                }
            } else {
                document.getElementById('the-canvas' + val).scrollIntoView();
            }
        } catch (err) {
            console.log('currentChange===>', err);
        }
    },


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容