React、pdfjs-dist、pubsub-js实现简易自定义PDF页面

本文详细介绍了如何利用pdfjs-dist库和pubsub-js消息订阅库,实现PDF文档的分页渲染、侧边略缩图列表、中心区域PDF的缩放以及页面滚动功能。关键步骤包括初始化工作、PDF页面的异步渲染、略缩图列表的生成、PDF区域的放大缩小以及页面跳转操作。代码示例展示了具体实现过程。

目录

0、简单效果展示

1、安装并使用 pdfjs-dist、pubsub-js,初始化工作

2、使用 pdfjs-dist 绘制给定PDF的首页或整体

3、侧边的略缩图列表

4、放大或缩小中心区域的 PDF

5、滚动到指定PDF页面的位置

6、点击左侧略缩图列表中的某略缩页,中间的展示区滚动到该页的位置

7、完整实现代码

 (7-1)displayPDF.js

(7-2)showPDF.jsx

 (7-3)showPDF.scss

(7-4)App.js

 参考:


0、简单效果展示

1、安装并使用 pdfjs-dist、pubsub-js,初始化工作

       安装pdfjs-dist:

npm i pdfjs-dist

        引入并配置工作者源文件:

import * as PDF from 'pdfjs-dist'

import workerSrc from 'pdfjs-dist/build/pdf.worker.entry'
PDF.GlobalWorkerOptions.workerSrc = workerSrc;

        安装 pubsub-js(消息订阅与发布):

npm i pubsub-js

        引入到项中:

import pubsub from 'pubsub-js'

        定义一个用于包裹绘制 pdf 的 canvas 列表的容器:

// 承装所有 canvas 的容器的类名
const allCanvasParentNodeClassName = 'canvas-parent-container';

2、使用 pdfjs-dist 绘制给定PDF的首页或整体

        封装函数 displayPDF 实现该功能。

        该函数接收六个参数:

        (1)给定 pdf 的地址;

        (2)承载要展示pdf区域的父元素(dom);

        (3)是否展示完整个pdf(false 就是只展示 pdf 的首页);

        (4)指定 canvas 的类名(PDF的每一页都会在一个canvas画布上绘制,为这一组画布确定一个共同的类名);

        (5)自定义pdf的缩放倍数;

        (6)pdf渲染完成后的一个回调函数。

/* 
    绘制整个pdf,一个canvas绘制一页
    传入 pdf 的地址、父容器标签元素、是否展示全部(false 即展示首页) 、指定canvas类名, 自定义缩放大小、自定义内联样式
*/
function displayPDF(pdfURL, parentNode, isAllPages, canvasClassName, customScale, callback){
    // 使用 PDF 加载指定的 .pdf 文档
    const loadingTask = PDF.getDocument(pdfURL);

    loadingTask.promise.then(async pdf=>{
        // pdf._pdfInfo.numPages 为 pdf 的总页数,isAllPage 为boolean值,表示是否展示全部
        const pageNumber = isAllPages ? pdf._pdfInfo.numPages : 1;

        /*         
            当前显示设备的物理像素分辨率与CSS像素分辨率之比,它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素
            <canvas>可能在视网膜屏幕上显得太模糊。 使用window.devicePixelRatio确定应添加多少额外的像素密度以使图像更清晰。 
        */
        const outputScale = window.devicePixelRatio || 1;

        const allCanvasParentNode = document.createElement('div');
        allCanvasParentNode.className = allCanvasParentNodeClassName;
        allCanvasParentNode.style = `
            width: 100%;
            height: 100%;
            overflow: auto;
            padding: 0;
            margin: 0;
            box-sizing: border-box;
            display: flex;
            flex-direction: column;
            align-items: center;
        `
        // 将 canvas 列表的容器添加到给定的容器中
        parentNode.appendChild(allCanvasParentNode);

        // 根据 isAllPages 确定渲染多少页
        mainAreaAsyncRender(pdf, pageNumber, 1, customScale, canvasClassName, outputScale, allCanvasParentNode, callback);
    })
}

        其中 mainAreaAsyncRender 函数为解决 pdf 页面异步渲染的一个方案。即 使 PDF 的每一页在渲染完成后,都可以直接呈现到页面上,不必等待 PDF 的所有页面都渲染完成再一次过呈现到页面上(这种一般直接使用 for 循环来完成),避免用户过多的等待。

        该函数接收以下几个参数:

        (1)使用 pdfjs-dist 的 getDocument 加载后进行期约构造成功后返回的 pdf 对象;

        (2)当前 pdf 的总页数;

        (3)当前所绘制的是第几页;

        (4)自定义的缩放比例;

        (5)自定义的 canvas 类名;

        (6)当前显示设备的物理像素分辨率与CSS像素分辨率之比;

        (7)承装所有 canvas 的容器(dom元素);

        (8)所有页面渲染完成后执行的回调函数(这里设置回调函数有一个参数,为该pdf的总页数,用于展示pdf的总页数);

// 主要展示区 异步渲染解决方案,使之一上来就有页面
async function mainAreaAsyncRender(pdf, pageNumber, i, customScale, canvasClassName, outputScale, allCanvasParentNode, callback){
    // 获取 pdf 指定页
    let page = await pdf.getPage(i);
    // 设置 canvas 整体缩放的倍数
    let scale = customScale || 1.4;
    let viewport = page.getViewport({ scale });

    // 创建 canvas 共同的容器
    let canvas = document.createElement('canvas');
    canvas.className = canvasClassName || '';
    let context = canvas.getContext("2d");

    // 设置 canvas 和内容 大小
    canvas.width = Math.floor(viewport.width * outputScale);
    canvas.height = Math.floor(viewport.height * outputScale);
    canvas.style.width = Math.floor(viewport.width) + "px";
    canvas.style.height = Math.floor(viewport.height) + "px";
    canvas.style.margin = '10px 0';

    let transform = (outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null);

    // 将指定页渲染到 canvas
    let renderContext = {
        canvasContext: context,
        transform,
        viewport,
    };
    page.render(renderContext);

    // 将当前 canvas(当前页) 添加到父容器中
    allCanvasParentNode.appendChild(canvas);

    i++;
    if(i <= pageNumber){
        requestAnimationFrame(()=>
            mainAreaAsyncRender(pdf, pageNumber, i, customScale, canvasClassName, outputScale, allCanvasParentNode, callback));
    }else{
        // 传入 pdf 总页数
        if(callback) callback(pdf._pdfInfo.numPages);

        // 发布渲染完成的消息
        pubsub.publish('renderFinish');
    }
};

        渲染完成后, 发布消息 ‘renderFinish’ 告诉组件渲染已完成。

3、侧边的略缩图列表

/* 
    侧边导航略缩图
    参数:pdf地址、父容器元素、中心展示区域设置的canvas的类名,设置当前canvas的类名,自定义当前canvas列表的缩放大小,回调函数
*/
function displayPDFLeftNav(pdfURL, parentNode, centerAreaCanvasClassName, canvasClassName, customScale){
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bodyHealthy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值