美文网首页
5、【最终篇】前端页面如何优雅的显示PDF:虚拟滚动

5、【最终篇】前端页面如何优雅的显示PDF:虚拟滚动

作者: 编程琐事 | 来源:发表于2020-04-12 15:54 被阅读0次

    推荐阅读

    上节回顾

    以上4节我们完成了PDF的预览,和基本功能的实现,前端渲染PDF的原理就是为每一页数据添加canvas,如果要渲染页面的文字就会在 canvas 同级添加一个 div 显示文本信息,如果pdf文件页数特别多的时候,就会重复操作以上操作,导致浏览器的回流和重绘。这就会造成性能浪费,导致页面卡顿。可以自己找一个几千页的PDF文件感受一下。

    有了以上的分析,我们就知道改如何优化PDF渲染,我们假设页面上面有一个列表,列表里面每一个元素都保存着pdf页面信息和文本信息,那么我们的优化就从 pdf 大文件优化转化成了前端页面长列表优化。

    如果是优化长列表,我们能列举出好多优化方案:懒加载,虚拟滚动。我们这节就用虚拟滚动优化PDF大文件 使用的依赖包是 react-virtualized

    react-virtualized

    List: 用法很简单,我们本节只列举出我们必要的一些属性

    • width

    列表的宽度约束,定义 pdf 视图的宽度

    • height

    列表的高度约束,定义 pdf 视图的宽度,如果超过这个高度都不进行 render,只做虚拟的dom

    • rowHeight

    行高,列表元素的高度,可以是一个数字类型或者是一个函数根据索引动态的获取每一个元素的高度

    • rowCount

    当前列表的元素总数

    • rowRenderer

    每行要渲染的内容

    • overscanRowCount

    在列表的可见范围之上/之下呈现的行数。这可以帮助减少在某些浏览器/设备上滚动时的闪烁

    • scrollToAlignment

    控制滚动到行的对齐方式 ('auto','start', 'end','center')

    • scrollToIndex

    滚动到行的索引,可以根据这个属性进行PDF的翻页

    • onRowsRendered

    使用有关刚刚渲染的行的信息调用回调,滚动的时候可以得到的信息{ overscanStartIndex, overscanStopIndex, startIndex:, stopIndex },在这个函数里,我们能够动态的渲染页面,每次只渲染一个页面。

    假如说,dom 中渲染的列表索引是 5 6 7 8 9

    1. overscanStartIndex dom 显示所列表的最上面索引 5
    2. overscanStopIndex dom 显示所列表的最下面索引 9
    3. startIndex: 当前渲染的列表索引
    4. stopIndex: 下一个需要渲染的索引

    *更多内容请参考gitHub: https://github.com/bvaughn/react-virtualized

    开始编码

    • 安装并使用
    yarn add react-virtualized
    
    import { List as VList } from 'react-virtualized';
    
    • 动态渲染页面

    PDF 文件每一页的宽和高都是不一样的所以我们需要一个数组保存文件的每一页数据

    const getPageInfo = async (curPdf) => {
        const page = await curPdf.getPage(1);
        const viewport = page.getViewport({ scale: scale * devicePixelRatio });
        const width = viewport.width;
        const height = viewport.height;
        const array = []
        for (let index = 0; index < curPdf.numPages; index++) {
            array.push({ width, height })
        }
        setNumPages(array)
    }
    

    通过 getPage 函数获取每一个的信息,这里我们只保存第一页的信息,在滚动的时候动态的修改这个数组,保证每一页的数据都是准确的。

    根据 react-virtualized List 文档我们定义一些属性值,编写如下代码

    <VList
        style={{
            margin: 'auto'
        }}
        overscanRowCount={3}
        scrollToAlignment="start"
        rowCount={numPages?.length}
        width={numPages[0].width}
        height={numPages[0].height}
        rowRenderer={renderItem}
        rowHeight={getItemHeight}
        scrollToIndex={currentPage - 1}
        onRowsRendered={({
            overscanStartIndex,
            overscanStopIndex,
            startIndex,
            stopIndex,
        }) => {
            renderPdf(startIndex, pdf)
            setCurrentPage(stopIndex + 1)
        }}
    />
    

    在 onRowsRendered 函数中,我们在滚动页面的时候,更新当前页码,同样更新渲染当前页面信息

    const renderItem = ({ key, index, style }) => (
        <div key={key} style={{ ...style, border: '1px solid #dddddd' }} data-index={index}>
            <canvas
                data-page-number={index + 1}
                style={{ width: style.width, height: style.height }}
            />
            <div
                data-page-number={index + 1}
                className="textLayer"
                style={{ width: style.width, height: style.height }}
            ></div>
        </div>
    )
    

    在渲染每一页的内容是,我们定义两个标签,canvas 和 div 分别用来显示 PDF 信息和文本信息。在渲染的时候,通过一下方法,把内容渲染到相应的标签里面

    # 渲染 pdf 信息
    const canvas = document.querySelector(`canvas[data-page-number='${num + 1}']`);
    ... 一系列操作(以上4节己经说过)
    // Render PDF page into canvas context
    const renderContext = {
        canvasContext: context,
        viewport: viewport
    };
    const renderTask = page.render(renderContext);
    
    # 渲染 文本信息
    const textLayerDiv = document.querySelector(`div[data-page-number='${num + 1}']`)
    if (textLayerDiv) {
        // 创建新的TextLayerBuilder实例
        const textLayer = new TextLayerBuilder({
            textLayerDiv,
            pageIndex: page.pageIndex,
            viewport,
        });
    
        textLayer.setTextContent(textContent);
    
        textLayer.render();
    }
    

    相关函数在之前的文章已经说过,就不再多说啦,这里不同的就是渲染到标签的方式不同,之前是通过js创建div,appendChild,插入到dom,这里是先把dom元素渲染出来,通过属性获得dom元素。

    • 实现翻页
      HTML 部分就不再说了,上一节已经说过了,这里只说一下如何实现翻页过程,

    PDF 保存的文件页面信息是从第一页开始的,List 把渲染的内容看成一个长列表,索引值是从0 开始的,通过改变属性 scrollToIndex 的索引实现翻页

    本节完整代码:https://github.com/LiuSandy/react-pdf-render


    到此我们由浅入深的把前端如何优雅的渲染PDF文件都已经讲完了,希望对您有所帮助,欢迎 + VX 公众号【编程琐事】。在 GitHub 给一个 Start 吧!!!

    相关文章

      网友评论

          本文标题:5、【最终篇】前端页面如何优雅的显示PDF:虚拟滚动

          本文链接:https://www.haomeiwen.com/subject/ojkumhtx.html