You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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);

该查看器可正常运行,但当文档尺寸大于屏幕(如手机端)时,文档会从侧边溢出,用户无法滚动查看隐藏部分:
PDF超出屏幕侧边溢出

直接将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 02:41:06