React中的交互式PDF查看:完整教程 – wiki词典


React 中的交互式 PDF 查看:完整教程

在现代 Web 开发中,我们经常需要在应用程序内直接展示 PDF 文档,而不是简单地提供一个下载链接。一个功能完善的 PDF 查看器不仅能渲染文档,还应该提供页面导航、缩放、旋转等交互功能,以提升用户体验。

本教程将详细介绍如何使用 react-pdf 这个强大的库,在 React 应用中从零开始构建一个功能丰富的交互式 PDF 查看器。

为什么选择 react-pdf

从头开始解析和渲染 PDF 是一项极其复杂的任务。幸运的是,开源社区已经为我们提供了成熟的解决方案。react-pdf 是一个广受欢迎的 React 封装库,它基于 Mozilla 开发的 pdf.js,提供了简单易用的 React 组件来处理 PDF。

主要优点:
* 易于集成: 以 React 组件的形式提供,与现有项目无缝集成。
* 功能强大: 支持分页、缩放、文本层、注释层等高级功能。
* 社区活跃: 拥有庞大的用户群体和持续的维护。
* 高度可定制: 你可以完全控制查看器的外观和行为。

准备工作

在开始之前,请确保你已经安装了 Node.js 和 npm/yarn。我们将使用 Vite 创建一个新的 React 项目。

1. 创建 React 项目

打开终端,运行以下命令:

“`bash

使用 Vite 创建一个新的 React + TypeScript 项目

npm create vite@latest my-pdf-viewer — –template react-ts

进入项目目录

cd my-pdf-viewer

安装依赖

npm install
“`

2. 安装 react-pdf

接下来,将 react-pdf 添加到你的项目中:

bash
npm install react-pdf

3. 准备一个 PDF 文件

为了进行测试,将一个示例 PDF 文件(例如 sample.pdf)放入项目的 public 文件夹中。这样我们可以通过 URL /sample.pdf 来访问它。

步骤一:基础 PDF 显示

首先,我们来实现最基本的功能:加载并显示 PDF 的第一页。

react-pdf 的工作方式依赖于一个 “worker”。我们需要告诉它从哪里加载这个 worker 文件。react-pdf 会自动从 node_modules 中提供这个文件。

修改你的 src/App.tsx 文件,内容如下:

“`tsx
import { useState }.tsx’;
import { Document, Page, pdfjs } from ‘react-pdf’;
import ‘react-pdf/dist/esm/Page/AnnotationLayer.css’;
import ‘react-pdf/dist/esm/Page/TextLayer.css’;

// 设置 PDF.js worker 的路径
pdfjs.GlobalWorkerOptions.workerSrc = //unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js;

const PDFViewer = () => {
const [numPages, setNumPages] = useState(null);
const [pageNumber, setPageNumber] = useState(1);

function onDocumentLoadSuccess({ numPages }: { numPages: number }) {
setNumPages(numPages);
}

return (



Page {pageNumber} of {numPages}

);
};

export default PDFViewer;
“`

代码解析:
1. 我们从 react-pdf 导入 DocumentPage 组件,以及 pdfjs 对象。
2. 关键步骤:我们通过 pdfjs.GlobalWorkerOptions.workerSrc 设置了 worker 的路径。这里我们使用了 unpkg CDN 来快速获取对应版本的 worker 文件,这是最简单可靠的方式。
3. 我们还导入了两个 CSS 文件,用于正确显示 PDF 的文本层和注释层。
4. <Document> 组件负责加载 PDF 文件。file 属性指向我们的 PDF 文件。onLoadSuccess 是一个回调函数,在 PDF 加载成功后触发,返回一个包含 numPages(总页数)的对象。
5. <Page> 组件用于渲染单页。pageNumber 属性指定了要显示的页码。
6. 我们使用 useState 来存储总页数和当前页码。

现在运行你的应用 (npm run dev),你应该能看到 sample.pdf 的第一页被成功渲染出来了。

步骤二:添加页面导航

一个只能看第一页的查看器是不完整的。接下来,我们添加“上一页”和“下一页”的控制功能。

我们来创建一个控制工具栏,并添加导航按钮。

“`tsx
import { useState } from ‘react’;
import { Document, Page, pdfjs } from ‘react-pdf’;
import ‘react-pdf/dist/esm/Page/AnnotationLayer.css’;
import ‘react-pdf/dist/esm/Page/TextLayer.css’;

pdfjs.GlobalWorkerOptions.workerSrc = //unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js;

const PDFViewer = () => {
const [numPages, setNumPages] = useState(null);
const [pageNumber, setPageNumber] = useState(1);

function onDocumentLoadSuccess({ numPages }: { numPages: number }) {
setNumPages(numPages);
setPageNumber(1); // 加载新文档时,重置到第一页
}

function goToPrevPage() {
setPageNumber((prevPageNumber) => Math.max(prevPageNumber – 1, 1));
}

function goToNextPage() {
if (numPages) {
setPageNumber((prevPageNumber) => Math.min(prevPageNumber + 1, numPages));
}
}

return (

{/ 控制工具栏 /}

<button onClick={goToPrevPage} disabled={pageNumber <= 1}>
上一页


第 {pageNumber} 页 / 共 {numPages ?? ‘–‘} 页

  {/* PDF 显示区域 */}
  <div style={{ border: '1px solid #ccc', width: '80%', height: '80vh', overflow: 'auto' }}>
    <Document
      file="/sample.pdf"
      onLoadSuccess={onDocumentLoadSuccess}
    >
      <Page pageNumber={pageNumber} />
    </Document>
  </div>
</div>

);
};

export default PDFViewer;
``
**新功能解析:**
1. 我们创建了
goToPrevPagegoToNextPage函数来更新pageNumber状态。
2. 我们添加了两个按钮,并使用
disabled属性来防止用户导航到无效的页码(例如小于1或大于总页数)。
3. 为了更好的视觉效果,我们将 PDF 包裹在一个有边框和可滚动的
div` 中。

步骤三:实现缩放功能

缩放是 PDF 查看器不可或缺的功能。react-pdf<Page> 组件提供了一个 scale 属性来控制渲染比例。

让我们在工具栏中加入缩放按钮。

“`tsx
// … (imports and worker setup remain the same)

const PDFViewer = () => {
const [numPages, setNumPages] = useState(null);
const [pageNumber, setPageNumber] = useState(1);
const [scale, setScale] = useState(1.0); // 新增 state 用于控制缩放

// … (onDocumentLoadSuccess, goToPrevPage, goToNextPage functions remain the same)

function zoomIn() {
setScale(prevScale => prevScale + 0.1);
}

function zoomOut() {
setScale(prevScale => Math.max(prevScale – 0.1, 0.5)); // 最小缩放比例为 0.5
}

return (

{/ 控制工具栏 /}

<button onClick={goToPrevPage} disabled={pageNumber <= 1}>
上一页


第 {pageNumber} 页 / 共 {numPages ?? ‘–‘} 页

    <div style={{ marginLeft: '20px' }}>
      <button onClick={zoomOut}>-</button>
      <span style={{ margin: '0 10px' }}>{(scale * 100).toFixed(0)}%</span>
      <button onClick={zoomIn}>+</button>
    </div>
  </div>

  {/* PDF 显示区域 */}
  <div style={{ border: '1px solid #ccc', width: '80%', height: '80vh', overflow: 'auto' }}>
    <Document
      file="/sample.pdf"
      onLoadSuccess={onDocumentLoadSuccess}
    >
      <Page 
        pageNumber={pageNumber} 
        scale={scale} // 应用缩放
      />
    </Document>
  </div>
</div>

);
};

export default PDFViewer;
“`

新功能解析:
1. 我们添加了一个新的 scale state,初始值为 1.0 (即 100%)。
2. 创建了 zoomInzoomOut 函数来增加或减少 scale 值。
3. 在工具栏中添加了对应的 “+” 和 “-” 按钮,并显示当前的缩放百分比。
4. 最重要的一步:将 scale state 作为 prop 传递给 <Page> 组件。

现在,你的 PDF 查看器已经具备了核心的交互能力!

步骤四:渲染所有页面(缩略图模式)

有时,用户可能希望一次性看到所有页面。我们可以通过循环来渲染所有的 <Page> 组件。

注意: 一次性渲染大量页面可能会消耗很多内存和性能。对于非常大的 PDF,这种方法需要谨慎使用或进行优化(例如,使用虚拟滚动)。

下面是一个渲染所有页面的简单示例:

“`tsx
// … (imports)

const PDFAllPagesViewer = () => {
const [numPages, setNumPages] = useState(null);

function onDocumentLoadSuccess({ numPages }: { numPages: number }) {
setNumPages(numPages);
}

return (


{Array.from(new Array(numPages ?? 0), (el, index) => (
page_${index + 1}}
pageNumber={index + 1}
scale={0.5} // 以较小的比例显示
renderTextLayer={false} // 关闭文本层以提高性能
renderAnnotationLayer={false} // 关闭注释层
/>
))}

);
};
“`
这个组件会以 50% 的比例渲染文档中的每一页,非常适合用作缩略图预览。

结论

通过本教程,我们使用 react-pdf 成功构建了一个功能齐全的交互式 PDF 查看器。我们实现了以下核心功能:
* 加载并渲染 PDF 文档。
* 实现上一页/下一页的页面导航。
* 实现放大/缩小的视图缩放。

react-pdf 还提供了更多高级功能,例如:
* 文本层(Text Layer):允许用户选择和复制 PDF 中的文本。我们在第一个例子中导入的 CSS (TextLayer.css) 就是为此服务的。
* 自定义加载动画:在 <Document> 组件加载时,你可以提供一个 loading prop 来显示自定义的加载指示器。
* 密码保护的 PDF:通过 onPassword 回调来处理需要密码的文档。
* 旋转页面<Page> 组件接受一个 rotate prop (值为 90, 180, 270)。

要了解更多信息,请务必查阅 react-pdf 官方文档。希望这篇教程能帮助你轻松地在 React 项目中集成强大的 PDF 功能!

滚动至顶部