如何使用pdf.js动态适配PDF显示,解决移动端溢出问题?
问题描述
我在React应用中集成了pdf.js实现PDF查看器,代码如下:
import React, { Component } from "react"; import { connect } from "react-redux"; import { Container, Row, Col, Button, Input } from "reactstrap"; import { searchBarStyle } from "../../common/reactSelectStyle"; export class PDFViewer extends Component { constructor(props) { super(props); this.state = { pdf: null, }; } componentDidMount() { var pdfjsLib = window["pdfjs-dist/build/pdf"]; var url = this.props.pdfUrl; // var loadingTask = pdfjsLib.getDocument(url); var loadingTask = pdfjsLib.getDocument({ url: url, disableAutoFetch: true, disableStream: true, }); loadingTask.promise.then((pdf) => { this.setState({ pdf }); }); } render() { if (!!this.state.pdf) { this.state.pdf.getPage(this.state.currentPage).then((page) => { var canvas = document.getElementById("pdf_renderer"); var ctx = canvas.getContext("2d"); var viewport = page.getViewport(this.state.zoom); canvas.width = viewport.width; canvas.height = viewport.height; page.render({ canvasContext: ctx, viewport: viewport, }); }); } return ( <Container> <Row> <Col> <div id="canvas_container" className="canvas-container"> <canvas id="pdf_renderer"> </canvas> </div> </Col> </Row> </Container> ); } } const mapStateToProps = (state) => ({}); const mapDispatchToProps = {}; export default connect(mapStateToProps, mapDispatchToProps)(PDFViewer);
该查看器可正常运行,但当文档尺寸大于屏幕(如手机端)时,文档会从侧边溢出,用户无法滚动查看隐藏部分:
直接将canvas尺寸设为窗口宽高不可行,也不合理:
canvas.width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; canvas.height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
这样会将超出屏幕的文档部分裁切。
需求:像PDF阅读器应用那样,保持文档正确的宽高比,同时完整显示在屏幕范围内,请问该如何解决?
解决方案
要实现保持PDF宽高比且完整适配屏幕,可通过以下步骤修改代码:
1. 完善组件状态与引用
先补充必要的状态变量,同时创建容器的React引用,用于获取容器实际宽度:
constructor(props) { super(props); this.state = { pdf: null, currentPage: 1, // 默认显示第一页 containerWidth: 0, // 容器宽度,用于计算适配比例 }; this.canvasContainerRef = React.createRef(); // 容器DOM引用 }
2. 监听容器尺寸变化
在组件挂载时初始化容器宽度,并监听窗口resize事件,确保窗口变化时重新计算适配逻辑:
componentDidMount() { const pdfjsLib = window["pdfjs-dist/build/pdf"]; const url = this.props.pdfUrl; const loadingTask = pdfjsLib.getDocument({ url: url, disableAutoFetch: true, disableStream: true, }); loadingTask.promise.then((pdf) => { this.setState({ pdf }); this.setContainerWidth(); // 监听窗口大小变化 window.addEventListener('resize', this.setContainerWidth); }); } componentWillUnmount() { // 移除监听,避免内存泄漏 window.removeEventListener('resize', this.setContainerWidth); } // 设置容器宽度的方法 setContainerWidth = () => { if (this.canvasContainerRef.current) { this.setState({ containerWidth: this.canvasContainerRef.current.clientWidth }, () => { // 宽度更新后重新渲染PDF this.renderPDFPage(); }); } }
3. 计算适配的缩放比例
编写方法计算能让PDF完整显示在容器内的缩放比例,严格保持原文档宽高比:
calculateFitZoom = (page) => { const { containerWidth } = this.state; const originalViewport = page.getViewport(1); // 以容器宽度为基准计算缩放比例,保证PDF宽度完全适配容器 return containerWidth / originalViewport.width; }
4. 重构PDF渲染逻辑
把渲染PDF的逻辑抽成单独方法,避免在React的render阶段执行异步操作和DOM修改:
renderPDFPage = () => { const { pdf, currentPage } = this.state; if (!pdf) return; pdf.getPage(currentPage).then((page) => { const canvas = document.getElementById("pdf_renderer"); const ctx = canvas.getContext("2d"); // 计算适配的缩放比例 const fitZoom = this.calculateFitZoom(page); const viewport = page.getViewport(fitZoom); canvas.width = viewport.width; canvas.height = viewport.height; page.render({ canvasContext: ctx, viewport: viewport, }); }); }
5. 更新组件渲染逻辑
修改render方法绑定容器引用,同时利用componentDidUpdate触发状态变化时的重新渲染:
componentDidUpdate(prevProps, prevState) { // 当PDF实例、当前页或容器宽度变化时,重新渲染PDF if (prevState.pdf !== this.state.pdf || prevState.currentPage !== this.state.currentPage || prevState.containerWidth !== this.state.containerWidth) { this.renderPDFPage(); } } render() { return ( <Container> <Row> <Col> <div id="canvas_container" className="canvas-container" ref={this.canvasContainerRef} style={{ overflow: 'auto' }} // 内容超出时显示滚动条 > <canvas id="pdf_renderer"></canvas> </div> </Col> </Row> </Container> ); }
关键说明
- 通过动态计算缩放比例,确保PDF宽度完全适配容器,同时保留原文档的宽高比,不会出现拉伸变形。
- 容器添加
overflow: auto样式,当PDF高度超出容器时,用户可通过垂直滚动查看完整内容。 - 把渲染逻辑从
render中抽离,避免React渲染周期内的异步操作冲突,保证组件运行稳定。
内容的提问来源于stack exchange,提问作者AG_HIHI
相关产品推荐
相关产品推荐

