react-pdf 实现 pdf 在线预览(翻页、放大缩小等)

原创文章,转载请注明出处,谢谢~
https://mp.weixin.qq.com/s/EyFKeMujSmcEPQXjYNBL8w

react实现pdf文档可翻页查看,可放大缩小及全屏等功能,选的是 react-pdf 插件, react-file-viewer 插件也试了下,可以查看pdf,不过是一次性加载全部,最终选了前者

插件引用

Ant Design Pro + react-pdf插件

插件地址:

https://www.npmjs.com/package/react-pdf

1、安装插件
$ npm install react-pdf 
# or
$ yarn add react-pdf
2、组件引用
import { Document, Page } from "react-pdf";
3、简单使用
<Document
  file="somefile.pdf"
  onLoadSuccess={onDocumentLoadSuccess}
>
  <Page pageNumber={pageNumber} />
</Document>
<p>Page {pageNumber} of {numPages}</p>

运行后报错:

解决:

import { Document, Page, pdfjs } from "react-pdf";
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;

可以加载出pdf即可

实现功能

先按自己想要的样式把界面搭好

一些功能主要是修改Page组件参数来达到想要的效果

<Page 
pageNumber={pageNumber}
width={pageWidth} 
/>
1、翻页、跳到指定页面

修改PagepageNumber属性值,加一些特定约束,比如已经翻到了第一页或者最后一页,输入的数值最小为1,最大为pdf的总页数值

2、放大缩小、一键适应窗口

修改Pagewidth属性值,如果单纯放大缩小,也可以用scale属性,不过跟宽度同时用会有点乱,索性只用了宽度

放大缩小过程中发现问题,页面居中用的flex布局,pdf放大溢出后,无法通过滚动条完全展示

子元素加了样式,已修复

width:max-content;
max-width:100%;
3、最终源码

样式:

.view {
  background:#444;
  display: flex;
  justify-content: center;
  height: 100vh;
  padding: 50px 0;
  overflow: auto;
}
.pageContainer {
  box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 4px 0px;
  width:max-content;
  max-width:100%;
}
.pageTool{
  position: absolute;
  bottom: 20px;
  background: rgb(66, 66, 66);
  color: white;
  padding: 8px 15px;
  border-radius: 15px;
  i{
    padding: 5px;
    margin:0 5px;
    &:hover{
      background: #333;
    }
  }
  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;
  }
}

js:

import React, { PureComponent } from 'react';
import { Icon, Spin, Tooltip,Input } from 'antd';
import styles from './File.less';

import { Document, Page, pdfjs } from "react-pdf";
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;

class File extends PureComponent {

  state = {
    pageNumber: 1,
    pageNumberInput: 1,
    pageNumberFocus: false,
    numPages: 1,
    pageWidth: 600,
    fullscreen: false
  };

  onDocumentLoadSuccess = ({ numPages }) => {
    this.setState({ numPages: numPages })
  }

  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 => {
    this.setState({ pageNumber: Number(e.target.value) })
  };

  pageZoomOut = () => {
    if (this.state.pageWidth <= 600) {
      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 })
    }
  }

  render() {
    const { pageNumber, pageNumberFocus, pageNumberInput,numPages, pageWidth, fullscreen } = this.state;
    return (
      <div className={styles.view}>
        <div className={styles.pageContainer}>
          <Document
            file="xxx.pdf"
            onLoadSuccess={this.onDocumentLoadSuccess}
            loading={<Spin size="large" />}
          >
            <Page pageNumber={pageNumber} width={pageWidth} loading={<Spin size="large" />} />
          </Document>
        </div>

        <div className={styles.pageTool}>
          <Tooltip title={pageNumber == 1 ? "已是第一页" : "上一页"}>
            <Icon type="left" onClick={this.lastPage} />
          </Tooltip>
          <Input value={pageNumberFocus ? pageNumberInput : pageNumber}
            onFocus={this.onPageNumberFocus}
            onBlur={this.onPageNumberBlur}
            onChange={this.onPageNumberChange}
            onPressEnter={this.toPage} type="number" /> / {numPages}
          <Tooltip title={pageNumber == numPages ? "已是最后一页" : "下一页"}>
            <Icon type="right" onClick={this.nextPage} />
          </Tooltip>
          <Tooltip title="放大">
            <Icon type="plus-circle" onClick={this.pageZoomIn} />
          </Tooltip>
          <Tooltip title="缩小">
            <Icon type="minus" onClick={this.pageZoomOut} />
          </Tooltip>
          <Tooltip title={fullscreen ? "恢复默认" : '适合窗口'}>
            <Icon type={fullscreen ? "fullscreen-exit" : 'fullscreen'} onClick={this.pageFullscreen} />
          </Tooltip>
        </div>
      </div>
    );
  }
}

export default props => (
  <File {...props} />
);

效果图

问题

1、请求pdf文件携带cookie

直接在 <Document file={{url:xxx,withCredentials:true}}/> 不行,要放到 state
见插件相关issues:
https://github.com/wojtekmaj/react-pdf/issues/308#issuecomment-443538284

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