前端實現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
複製程式碼