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

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


参考资料: mozilla/pdf.jshtml

头部引入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标签为不可手动缩放的react

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>
        );
    }
}
复制代码

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

React.PropTypes的问题

因为React v15.5起,React.PropTypes已移至另外一个包中。 React.PropTypes请改用prop-types库。 prop-types的使用以下:github

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
复制代码
相关文章
相关标签/搜索