react实现移动端PDF在线预览插件

本文介绍如何使用mozilla/pdf.js实现在前端应用中预览PDF文件的方法。通过封装React组件,可以轻松地将PDF阅读功能集成到现有项目中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前端实现DPF在线预览的插件比较多,不过大多都不够成熟,用起来可能觉得没那么好用;选了一个个人觉得相对好用,适合我们现有框架的mozilla/pdf.js


参考资料: mozilla/pdf.js

头部引入mozilla/pdf.js的库

这个库目前没有用npm安装的,直接在头部引入线上的或者下载一个就可以用了

<!DOCTYPE html>
<html lang="cn">
    <head>
        <meta charset="UTF-8">
        <meta content="yes" name="apple-mobile-web-app-capable">
        <meta content="yes" name="apple-touch-fullscreen">
        <meta content="telephone=no,email=no" name="format-detection">
        <title><%= htmlWebpackPlugin.options.title %></title>
        <link rel="stylesheet" href="./iconfont/iconfont.css">
        <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
        <script src="//mozilla.github.io/pdf.js/build/pdf.js"></script>
    </head>
    <body>
        <div id="appContainer"></div>
    </body>
</html>
复制代码

封装一个PDF组件,直接复制粘贴这段代码就可以

class PDF extends React.Component {
    constructor (props) {
        super(props)
        this.state = {
            pdf: null,
            scale: 1.2
        }
    }
    getChildContext () {
        return {
            pdf: this.state.pdf,
            scale: this.state.scale
        }
    }
    componentDidMount () {
        PDFJS.getDocument(this.props.src).then((pdf) => {
            console.log(pdf)
            this.setState({ pdf })
        })
    }
    render () {
    return (<div className='pdf-context'>{this.props.children}</div>) 
    }
}

PDF.propTypes = {
    src: React.PropTypes.string.isRequired
}

PDF.childContextTypes = {
    pdf: React.PropTypes.object,
    scale: React.PropTypes.number
}

class Page extends React.Component {
    constructor (props) {
        super(props)
        this.state = {
            status: 'N/A',
            page: null,
            width: 0,
            height: 0
        }
    }
    shouldComponentUpdate (nextProps, nextState, nextContext) {
        return this.context.pdf != nextContext.pdf || this.state.status !== nextState.status
    }
    componentDidUpdate (nextProps, nextState, nextContext) {
        this._update(nextContext.pdf) 
    }
    componentDidMount () {
        this._update(this.context.pdf) 
    }
    _update (pdf) {
        if (pdf) {
            this._loadPage(pdf)
        } else {
            this.setState({ status: 'loading' }) 
        }
    }
    _loadPage (pdf) {
        if (this.state.status === 'rendering' || this.state.page != null) return; 
        pdf.getPage(this.props.index).then(this._renderPage.bind(this))
        this.setState({ status: 'rendering' })
    } 
    _renderPage (page) {
        console.log(page)
        let { scale } = this.context 
        let viewport = page.getViewport(scale)
        let { width, height } = viewport
        let canvas = this.refs.canvas
        let context = canvas.getContext('2d')
        console.log(viewport.height, viewport.width)
        canvas.width = width
        canvas.height = height
        
        page.render({
            canvasContext: context,
            viewport
        })
      
        this.setState({ status: 'rendered', page, width, height })
    }
    render () {
        let { width, height, status } = this.state
        return (
            <div className={`pdf-page {status}`} style={{width, height}}>
                <canvas ref='canvas' />
            </div>
        )
    }
}

Page.propTypes = {
    index: React.PropTypes.number.isRequired
}
Page.contextTypes = PDF.childContextTypes

class Viewer extends React.Component {
    render () {
        let { pdf } = this.context
        let numPages = pdf ? pdf.pdfInfo.numPages : 0
        let fingerprint = pdf ? pdf.pdfInfo.fingerprint : 'none'
        let pages = Array.apply(null, { length: numPages })
            .map((v, i) => (<Page index={i + 1} key={`${fingerprint}-${i}`}/>))
        
        return (
            <div className='pdf-viewer'>
                {pages}
            </div>
        )
    }
}
Viewer.contextTypes = PDF.childContextTypes
复制代码

最后一步就是调用这个PDF组件就可以了,这里需要改一下头部的meta[name="viewport"]的属性为可自由放大缩小,退出PDF页时需要恢复meta标签为不可手动缩放的

class previewPDF extends React.Component {
    constructor (props) {
        super (props);
    }

    // 初始化PDF组件时改变meta为可手动缩放
    componentDidMount(){
        document.querySelector('meta[name="viewport"]').setAttribute("content", "width=device-width,user-scalable=yes,initial-scale=0.5,maximum-scale=1.2,minimum-scale=0.1");
    }

    // 组件卸载时恢复meta
    componentWillUnmount(){
        document.querySelector('meta[name="viewport"]').setAttribute("content", "width=device-width,user-scalable=no,initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5");
        window.location.reload();
    }

    render() {
        let PDF_URL = getQueryString('pdf', this.props.location.search);
        return (
            <PDF src={PDF_URL}>
                <Viewer />
            </PDF>
        );
    }
}
复制代码

-------------------更新内容------------------

React.PropTypes的问题

由于React v15.5起,React.PropTypes已移至另一个包中。 React.PropTypes请改用prop-types库。 prop-types的使用如下:

import PropTypes from 'prop-types';

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: PropTypes.string
};
复制代码

更新后的PDF组件

import React, { Component } from 'react';
import PropTypes from 'prop-types';

export class PDF extends Component {
    constructor (props) {
        super(props)
        this.state = {
            pdf: null,
            scale: 1.2
        }
    }
    getChildContext () {
        return {
            pdf: this.state.pdf,
            scale: this.state.scale
        }
    }
    componentDidMount () {
        PDFJS.getDocument(this.props.src).then((pdf) => {
            console.log(pdf)
            this.setState({ pdf })
        })
    }
    render () {
        return (<div className='pdf-context'>{this.props.children}</div>) 
    }
}

// PDF.propTypes = {
//     src: PropTypes.string.isRequired
// }

PDF.childContextTypes = {
    pdf: PropTypes.object,
    scale: PropTypes.number
}

export class Page extends Component {
    constructor (props) {
        super(props)
        this.state = {
            status: 'N/A',
            page: null,
            width: 0,
            height: 0
        }
    }
    // shouldComponentUpdate (nextProps, nextState, nextContext) {
    //     return this.context.pdf != nextContext.pdf || this.state.status !== nextState.status
    // }
    // componentDidUpdate (nextProps, nextState, nextContext) {
    //     debugger
    //     this._update(nextContext.pdf) 
    // }
    componentDidMount () {
        this._update(this.context.pdf) 
    }
    _update (pdf) {
        if (pdf) {
            this._loadPage(pdf)
        } else {
            this.setState({ status: 'loading' }) 
        }
    }
    _loadPage (pdf) {
        if (this.state.status === 'rendering' || this.state.page != null) return; 
        pdf.getPage(this.props.index).then(this._renderPage.bind(this))
        this.setState({ status: 'rendering' })
    } 
    _renderPage (page) {
        let { scale } = this.context 
        let viewport = page.getViewport(scale)
        let { width, height } = viewport
        let canvas = this.refs.canvas
        let context = canvas.getContext('2d')

        canvas.width = width
        canvas.height = height
        
        page.render({
            canvasContext: context,
            viewport
        })
      
        this.setState({ status: 'rendered', page, width, height })
    }
    render () {
        let { width, height, status } = this.state
        return (
            <div className={`pdf-page {status}`} style={{width, height}}>
                <canvas ref='canvas' />
            </div>
        )
    }
}

// Page.propTypes = {
//     index: PropTypes.number.isRequired
// }
Page.contextTypes = PDF.childContextTypes

export class Viewer extends Component {
    render () {
        let { pdf } = this.context
        let numPages = pdf ? pdf.pdfInfo.numPages : 0
        let fingerprint = pdf ? pdf.pdfInfo.fingerprint : 'none'
        let pages = Array.apply(null, { length: numPages })
            .map((v, i) => (<Page index={i + 1} key={`${fingerprint}-${i}`}/>))
        
        return (
            <div className='pdf-viewer'>
                {pages}
            </div>
        )
    }
}
Viewer.contextTypes = PDF.childContextTypes
复制代码

转载于:https://juejin.im/post/5ab0dc45518825558a067532

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值