React_在线预览pdf
概述
PDF 文档的预览功能在日常项目中很常见,今天我们就是使用 Reactjs + React-PDF 实现一个 PDF 文档在线预览功能,先来看看实现后的效果
PDF 文档在线预览实现的功能有
- PDF 文档预览功能
- 上下翻页功能
- 输入页面跳转功能
- PDF 文档放大缩小功能
功能实现
项目初始化
项目使用 Reactjs + React-PDF 实现,所以我们先来初始化一 React 个项目。
终端中输入命令 pnpm create vite reactjs-pdf-preview -- --template react
进行项目的初始化,接着我们 cd 进入项目根目录,执行命令 pnpm install
安装依赖包, 依赖包安装完成后,我们可以先启动项目,看看项目是否运行正常,输入命令 pnpm run dev
vite v2.9.9 dev server running at:
> Local: http://localhost:3000/
> Network: use `--host` to expose
ready in 468ms.
复制代码
可以看到在终端中输出了如上内容,在浏览器地址输入上述地址,可以看到初次运行的页面,说明项目启动正常,接着我们来添加插件 React-PDF
终端中输入
npm install react-pdf
or
yarn add react-pdf
or
pnpm install react-pdf
复制代码
插件安装完毕后,我们在 src 目录下新建一个文件
src/components/pdfPreview.js
文件建好后,我们添加一个预览 PDF 的类,如下内容
import React, { PureComponent } from 'react';
export default class PdfPreview extends PureComponent {
render(){
return (
<div className="pdf-view"></div>
)
}
}
复制代码
接着我们引入 PDF 预览插件的内容
import React, { PureComponent } from 'react';
import styles from './file.css';
import { Document, Page, pdfjs } from "react-pdf";
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
export default class PdfPreview extends PureComponent {
render () {
const { prfUrl } = this.props
return (
<div className="pdf-view">
<Document
file={prfUrl}
onLoadSuccess={this.onDocumentLoadSuccess}
loading={'加载中...'}
>
<Page pageNumber={pageNumber} width={pageWidth} loading={'加载中...'} />
</Document>
</div>
);
}
}
复制代码
PDF 文件由外部传入,可以作为一个公共组件在需要的地方引用
我们在 App.jsx 文件中引入,进行组件的测试,
import PdfPreview from "./components/pdf"
import pdf from "./Javascript.pdf"
import './App.css'
function App() {
return (
<div className="App">
<PdfPreview prfUrl={pdf} />
</div>
)
}
export default App
复制代码
可以看到 PDF 加载成功,但是做到这样还仅仅是不够的,我们下一步对他进行优化下,添加翻页按钮,放大缩小按钮和页码跳转功能,并对展示的样式进行优化
样式优化
我们添加如下代码,并新建一个 css 文件,用来存储我们的样式文件,
<div className="pdf-view">
<div className="container">
<Document file={prfUrl} > </Document>
</div>
<div className="page-tool">
<div className='page-tool-item'> 上一页</div>
<div className='page-tool-item'> 下一页</div>
<div className="input"> <input type="number" /> / </div>
<div className='page-tool-item'> 放大</div>
<div className='page-tool-item' > 缩小</div>
</div>
</div>
复制代码
.pdf-view {
display: flex;
justify-content: center;
padding: 30px 0 50px;
overflow: hidden;
box-sizing: border-box;
height: 100vh;
position: relative;
}
.container {
box-shadow: rgb(0 0 0 / 20%) 0px 2px 4px 0px;
width: max-content;
margin: 0 auto;
overflow: scroll;
box-sizing: border-box;
}
.page-tool {
position: absolute;
bottom: 35px;
padding-left: 15px;
padding-right: 15px;
display: flex;
align-items: center;
background: rgb(66, 66, 66);
color: white;
border-radius: 19px;
}
.page-tool-item {
padding: 8px 15px;
padding-left: 10px;
cursor: pointer;
}
input {
display: inline-block;
width: 50px;
text-align: center;
margin-right: 10px;
height: 24px;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
}
input[type="number"] {
-moz-appearance: textfield;
}
复制代码
样式添加完后,可以看到页面是这样的
我们接下来继续完善 PDF 预览功能
功能完善
我们在 state 中分别定义如下变量
pageNumber: 1,
pageNumberInput: 1,
pageNumberFocus: false,
numPages: 1,
pageWidth: 503,
fullscreen: false
复制代码
- pageNumber: 1 //页码
- pageNumberInput: 1 // input 显示的值
- pageNumberFocus: false // 是否获得焦点
- numPages: 1 // 总页数
- pageWidth: 503 // 预览pdf的宽度
- fullscreen: false // 是否全屏
这里我们分别定义如下函数,用来实现上下翻页,放大缩小,窗口全屏,以及页码跳转功能
lastPage = () => {
if (this.state.pageNumber == 1) {
return;
}
const page = this.state.pageNumber - 1;
this.setState({ pageNumber: page, pageNumberInput: page });
};
nextPage = () => {
if (this.state.pageNumber == this.state.numPages) {
return;
}
const page = this.state.pageNumber + 1;
this.setState({ pageNumber: page, pageNumberInput: page });
};
onPageNumberFocus = (e) => {
this.setState({ pageNumberFocus: true });
};
onPageNumberBlur = (e) => {
this.setState({
pageNumberFocus: false,
pageNumberInput: this.state.pageNumber,
});
};
onPageNumberChange = (e) => {
let value = e.target.value;
value = value <= 0 ? 1 : value;
value = value >= this.state.numPages ? this.state.numPages : value;
this.setState({ pageNumberInput: value });
};
toPage = (e) => {
if (e.keyCode === 13) {
this.setState({ pageNumber: Number(e.target.value) });
}
};
pageZoomOut = () => {
if (this.state.pageWidth <= 503) {
return;
}
const pageWidth = this.state.pageWidth * 0.8;
this.setState({ pageWidth: pageWidth });
};
pageZoomIn = () => {
const pageWidth = this.state.pageWidth * 1.2;
this.setState({ pageWidth: pageWidth });
};
pageFullscreen = () => {
if (this.state.fullscreen) {
this.setState({ fullscreen: false, pageWidth: 600 });
} else {
this.setState({ fullscreen: true, pageWidth: window.screen.width - 40 });
}
};
复制代码
这里要说的是 toPage
函数,需要判断下 keyCode
键盘事件作下处理
然后分别将事件绑定到按钮上,绑定时间后哦的代码如下
<div className="page-tool">
<div className="page-tool-item" onClick={this.lastPage}>
{" "}
上一页
</div>
<div className="page-tool-item" onClick={this.nextPage}>
{" "}
下一页
</div>
<div className="input">
<input
value={pageNumberFocus ? pageNumberInput : pageNumber}
onFocus={this.onPageNumberFocus}
onBlur={this.onPageNumberBlur}
onChange={this.onPageNumberChange}
onKeyDown={this.toPage}
type="number"
/>{" "}
/ {numPages}
</div>
<div className="page-tool-item" onClick={this.pageZoomIn}>
{" "}
放大
</div>
<div className="page-tool-item" onClick={this.pageZoomOut}>
{" "}
缩小
</div>
<div className="page-tool-item" onClick={this.pageFullscreen}>
{fullscreen ? "恢复默认" : "适合窗口"}
</div>
</div>
复制代码
事件绑定后我们测试发现,初始加载时显示的不够友好,这里我们优化一下,使用 React-PDF 的 家长事件 onLoadSuccess
添加一个函数,PDF
在加载完成后会调用这个函数,我们在这个函数中初始化页码
onDocumentLoadSuccess = ({ numPages }) => {
this.setState({ numPages: numPages });
};
复制代码
另外添加 loading
状态,这里可以使用加载动画,也可以使用文字
<div className="container">
<Document
file={prfUrl}
onLoadSuccess={this.onDocumentLoadSuccess}
loading={"加载中..."}
>
<Page
pageNumber={pageNumber}
width={pageWidth}
loading={"加载中..."}
/>
</Document>
</div>
复制代码
写到这里,PDF
文件预览组件我们已经写的差不多了,当然传入的文件不一定 PDF
文件地址,也可以是 base64
格式的内容,
如果预览地址是线上地址,需要带 cookie
的,可以在 state
中设置 然后传递进去
{ url: 'http://example.com/sample.pdf', httpHeaders: { 'X-CustomHeader': '40359820958024350238508234' }, withCredentials: true }