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

Angular/PDFJS多PDF文件共用单滚动条技术问询

解决Angular中多PDF共用一个滚动条的问题

问题根源

之前的自定义PDF Viewer组件每个都封装了独立的滚动容器(带overflow-y: auto的元素),导致多个PDF渲染时各自占用独立滚动区域,不仅出现多个滚动条,还可能因布局冲突导致内容重叠。核心解决思路是将所有PDF页面统一渲染到同一个全局滚动容器中,取消每个组件的独立滚动逻辑。

实现步骤

1. 重构为全局滚动容器 + 页面渲染服务

创建一个父组件作为全局滚动容器,同时封装PDF渲染服务负责处理单个PDF的页面生成,将所有PDF的页面统一插入到全局容器中。

父组件(全局滚动容器)

模板(pdf-container.component.html)

<div class="unified-scroll-wrapper">
  <div #pdfContentWrapper></div>
</div>

样式(pdf-container.component.scss)

.unified-scroll-wrapper {
  height: 100vh; /* 根据需求设置固定高度或最大高度 */
  overflow-y: auto;
  padding: 1rem;
  background-color: #f5f5f5;
}

#pdfContentWrapper {
  width: fit-content;
  margin: 0 auto;
}

逻辑(pdf-container.component.ts)

import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';
import { PdfRendererService } from './pdf-renderer.service';

@Component({
  selector: 'app-unified-pdf-viewer',
  templateUrl: './pdf-container.component.html',
  styleUrls: ['./pdf-container.component.scss']
})
export class UnifiedPdfViewerComponent implements OnInit {
  @ViewChild('pdfContentWrapper') contentWrapper!: ElementRef<HTMLDivElement>;
  // 待展示的PDF文件列表
  pdfFileUrls = [
    '/assets/docs/report1.pdf',
    '/assets/docs/report2.pdf',
    '/assets/docs/manual.pdf'
  ];

  constructor(private pdfRenderer: PdfRendererService) {}

  async ngOnInit(): Promise<void> {
    await this.renderAllPdfs();
  }

  private async renderAllPdfs(): Promise<void> {
    const wrapperEl = this.contentWrapper.nativeElement;

    for (const url of this.pdfFileUrls) {
      try {
        // 渲染单个PDF的所有页面
        const pageElements = await this.pdfRenderer.renderPdfToElements(url);
        // 将页面元素添加到全局容器
        pageElements.forEach(page => wrapperEl.appendChild(page));
        // 可选:添加PDF间的分隔线
        wrapperEl.appendChild(this.createDivider());
      } catch (error) {
        console.error(`加载PDF失败: ${url}`, error);
        wrapperEl.appendChild(this.createErrorElement(url));
      }
    }
  }

  private createDivider(): HTMLElement {
    const divider = document.createElement('div');
    divider.style.height = '1.5rem';
    divider.style.borderBottom = '1px dashed #ccc';
    divider.style.margin = '1rem 0';
    return divider;
  }

  private createErrorElement(url: string): HTMLElement {
    const errorEl = document.createElement('div');
    errorEl.textContent = `无法加载PDF文件: ${url}`;
    errorEl.style.color = '#dc3545';
    errorEl.style.padding = '1rem';
    errorEl.style.background = '#f8d7da';
    errorEl.style.borderRadius = '4px';
    return errorEl;
  }
}

2. 封装PDF渲染服务

将PDFJS的渲染逻辑抽离为服务,负责将单个PDF转换为可插入的DOM元素(canvas),不包含任何滚动容器。

pdf-renderer.service.ts

import { Injectable } from '@angular/core';
import * as pdfjsLib from 'pdfjs-dist';
import 'pdfjs-dist/build/pdf.worker.min';

// 配置PDFJS Worker路径,根据项目实际CDN或本地文件调整
pdfjsLib.GlobalWorkerOptions.workerSrc = '//cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';

@Injectable({ providedIn: 'root' })
export class PdfRendererService {
  // 渲染单个PDF,返回页面canvas元素数组
  async renderPdfToElements(pdfUrl: string, scale = 1.5): Promise<HTMLElement[]> {
    const pdfDoc = await pdfjsLib.getDocument(pdfUrl).promise;
    const pageCount = pdfDoc.numPages;
    const pageElements: HTMLElement[] = [];

    for (let pageNum = 1; pageNum <= pageCount; pageNum++) {
      const page = await pdfDoc.getPage(pageNum);
      const viewport = page.getViewport({ scale });

      // 创建canvas用于渲染页面
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      if (!ctx) continue;

      canvas.width = viewport.width;
      canvas.height = viewport.height;
      canvas.style.display = 'block';
      canvas.style.marginBottom = '0.5rem';

      // 渲染页面到canvas
      await page.render({
        canvasContext: ctx,
        viewport
      }).promise;

      pageElements.push(canvas);
    }

    return pageElements;
  }
}

3. 移除原自定义组件的独立滚动逻辑

如果之前有单独的pdf-viewer组件,需要删除其内部的滚动容器(比如带有overflow-y: auto的div),改为仅负责页面渲染,或者直接弃用该组件,使用上述服务来生成页面元素。

关键注意事项

  • 容器高度设置:全局滚动容器必须设置固定高度或最大高度,否则overflow-y: auto不会生效
  • 性能优化:如果PDF数量多或单PDF页数多,可实现滚动加载(监听容器滚动事件,当滚动到底部时再渲染下一个PDF)
  • 样式调整:可根据需求修改页面缩放比例、间距、背景色等,确保页面布局整齐
  • 错误处理:添加PDF加载失败的捕获逻辑,避免单个PDF加载失败影响其他文件

内容的提问来源于stack exchange,提问作者Sushant Vaidya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 00:25:26