"pdfjs-dist": "^2.2.228" "alloyfinger": "^0.1.7", "css3transform": "^1.2.3"
安裝命令css
npm i pdfjs-dist npm i alloyfinger@0.1.7 npm install css3transform
import React, {Component} from 'react'; import PropTypes from 'prop-types'; import PDFJS from 'pdfjs-dist'; import styles from './index.scss'; import {isPdfFile} from './../../utils/utils'; import AlloyFinger from 'alloyfinger'; import Transform from 'css3transform'; import {ActivityIndicator} from "antd-mobile"; import pdfWorkerEntryJs from 'pdfjs-dist/build/pdf.worker.entry'; // pdf.worker.js入口文件 // PDFJS.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${PDFJS.version}/pdf.worker.js`; PDFJS.GlobalWorkerOptions.workerSrc = pdfWorkerEntryJs; export class PdfLoading extends Component { render() { const {loading, loadingText} = this.props; return ( <div> <ActivityIndicator toast text={loadingText ? loadingText : '文件加載中...'} animating={loading} /> </div> ); } } export class PDF extends Component { constructor(props) { super(props) this.state = { pdf: null, // pdf 對象 scale: 1.2, // 渲染規模 isPdfFileTrue: true, // pdf 文件是否正確獲取 fileErrorText: '', // 錯誤提示語 loadingText: '文件加載中...', // 加載文案 loading: false, // 是否加載 } } getChildContext() { const {pdf, scale} = this.state; return { pdf, scale } } componentWillMount() { this.setState({ loadingText: '文件加載中...', // 加載文案 loading: true, // 是否加載 }) } componentDidMount() { const {src} = this.props; // 不是pdf文件不能繼續渲染 if (!isPdfFile(src)) { this.setState({ isPdfFileTrue: false, fileErrorText: '無效或損壞的 PDF 文件', loadingText: '文件加載中...', // 加載文案 loading: false, // 是否加載 }) return; } //獲取pdf文件流 let loadingTask = PDFJS.getDocument(src); let _this = this; loadingTask.promise.then(function (pdf) { if (pdf) { _this.setState({ pdf, loadingText: '文件加載中...', // 加載文案 loading: false, // 是否加載 }) } else { _this.setState({ isPdfFileTrue: false, loadingText: '文件加載中...', // 加載文案 loading: false, // 是否加載 }) } }, function (exception) { let message = exception && exception.message; console.log('message--' + message); let loadingErrorMessage = ''; if (exception instanceof PDFJS.InvalidPDFException) { loadingErrorMessage = '無效或損壞的 PDF 文件'; } else if (exception instanceof PDFJS.MissingPDFException) { loadingErrorMessage = '缺乏 PDF 文件'; } else if (exception instanceof PDFJS.UnexpectedResponseException) { loadingErrorMessage = '意外的服務器響應錯誤'; } else { loadingErrorMessage = '載入 PDF 時發生錯誤, 緣由多是文件跨域'; } _this.setState({ isPdfFileTrue: false, fileErrorText: loadingErrorMessage, loadingText: '文件加載中...', // 加載文案 loading: false, // 是否加載 }) }) } render() { const {isPdfFileTrue, fileErrorText, loadingText, loading} = this.state; const {children} = this.props; return ( <div className={styles.pdfContext}> {isPdfFileTrue ? children : fileErrorText} <PdfLoading loadingText={loadingText} loading={loading}/> </div> ) } } PDF.childContextTypes = { pdf: PropTypes.object, scale: PropTypes.number } export class Page extends Component { constructor(props) { super(props) this.state = { status: 'N/A',// 加載pdf的狀態 'N/A':初始狀態 'loading':加載中 'rendering':渲染中 'rendered':渲染完成 page: null, // pdf頁面 width: 0, // pdf頁面寬度 height: 0, // pdf頁面高度 loadingText: '文件加載中...', // 加載文案 loading: false, // 是否加載 } } componentDidMount() { this._update(this.context.pdf); } _update(pdf) { if (pdf) { this._loadPage(pdf); this.setState({ loadingText: '文件加載中...', loading: true, }) } else { this.setState({ status: 'loading', loadingText: '文件加載中...', loading: false, }) } } _loadPage(pdf) { const {status, page} = this.state; if (status === 'rendering' || page !== null) { return; } const {index} = this.props; pdf.getPage(index).then(this._renderPage.bind(this)) this.setState({ status: 'rendering', loadingText: '文件渲染中...', loading: true, }) } // canvas 大小在瀏覽器有限制,須要變小 getViewportFunc = (page, scale) => { let viewport = page.getViewport(scale); let {width, height} = viewport; if (width > 2048 || height > 2048) { let scaleTemp = Math.ceil((width > height ? width : height) / 2048); let currentScale = scale / scaleTemp / 2; // 應渲染的規模比例 return page.getViewport(currentScale); } else { return viewport; } } _renderPage(page) { let {scale} = this.context; let viewport = this.getViewportFunc(page, scale); let {width, height} = viewport; let canvas = this.refs.canvas; let context = canvas.getContext('2d'); // 高清顯示pdf canvas.width = width * 2; canvas.height = height * 2; canvas.style.width = `${width}px`; canvas.style.height = `${height}px`; canvas.style.overflow = 'scroll'; context.scale(2, 2); page.render({ canvasContext: context, viewport }) this.setState({ status: 'rendered', loadingText: '文件渲染完成...', loading: false, page, width, height, }) } render() { let {width, height, status, loadingText, loading} = this.state; console.log('status--' + status); return ( <div className={styles.pdfPage} style={{width, height, marginBottom: 10}}> <canvas ref='canvas'/> <PdfLoading loadingText={loadingText} loading={loading}/> </div> ) } } Page.contextTypes = PDF.childContextTypes export class Viewer extends Component { handlePinch = () => { let pdfViewer = this.refs.pdfViewer; Transform(pdfViewer); let initScale = 1; let pdfViewerAf = new AlloyFinger(pdfViewer, { pinch: (evt) => { console.log('pinch'); } }); pdfViewerAf.on('pinch', (evt) => { const {zoom} = evt; let zoomTemp = 0; // 限制縮放比例爲 0.2~1.6 if (zoom < 0.2) { zoomTemp = 0.2; } else if (zoom > 1.6) { zoomTemp = 1.6; } else { zoomTemp = zoom; } pdfViewer.scaleX = pdfViewer.scaleY = initScale * zoomTemp; }); } componentDidMount() { this.handlePinch(); // 手勢放大縮小 pdf } render() { let {pdf = {}} = this.context; let numPages = pdf ? pdf.numPages : 0; let fingerprint = pdf ? pdf.fingerprint : 'none'; let pages = Array.apply(null, {length: numPages}) .map((v, i) => (<Page index={i + 1} key={`${fingerprint}-${i}`}/>)) return ( <div className={styles.pdfViewer} ref='pdfViewer'> {pages} </div> ) } } Viewer.contextTypes = PDF.childContextTypes;
// index.scss .pdfContext { float: left; width: 100%; height: 100%; } .pdfPage { float: left; width: 100%; height: 100%; } .pdfViewer { float: left; width: 100%; height: 100%; }
// utils/utils.js export function isPdfFile(pdf) { if (!pdf) { return false; } if (!/\.(pdf)$/.test(pdf.toLowerCase())) { return false; } else { return true; } }
import {PDF, Viewer} from '../../components/pdfViewer'; return ( <div className={styles.pdfWrap}> <PDF src={pdfFileLink}> <Viewer/> </PDF> </div> );
.pdfWrap { position: relative; width: 100%; height: 100%; top: 0; bottom: 0; left: 0; right: 0; text-align: center; background: #F8F8F8; line-height: 0.6rem; font-size: 0.32rem; overflow: scroll; }