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

如何在Angular 4+中实现PDF文档对比的滚动同步功能

Angular 4+ 实现文档对比滚动同步功能

要实现类似Draftable的滚动同步(滚动一侧文档,另一侧自动定位到对应内容,即使两份文档存在差异),核心是先建立两份文档内容的位置映射关系,再基于这个映射处理滚动同步逻辑,具体步骤如下:

一、生成内容位置映射表

这是实现精准同步的关键——你需要先对比两份文档的内容,找出对应内容块在左右文档中的滚动位置对应关系。

  • 如果是纯文本/HTML文档:可以用diff-match-patch这类差异对比库,找出两份文档中匹配的段落/文本块,记录每个块在左侧文档的top位置(距离容器顶部的距离)和右侧文档对应的top位置,形成一个映射数组,比如:
    interface ScrollMapItem {
      leftTop: number;
      rightTop: number;
      leftHeight: number;
      rightHeight: number;
    }
    const scrollMap: ScrollMapItem[] = [
      { leftTop: 0, rightTop: 0, leftHeight: 100, rightHeight: 120 },
      // 更多匹配块的映射...
    ];
    
  • 如果是PDF文档:先用pdfjs-dist解析PDF,提取每页的文本和布局信息(每个文本块的位置、高度),再对比生成上述映射表。

二、Angular组件中实现滚动同步

1. 模板结构

给左右两个文档容器添加标识,方便获取DOM元素:

<div class="document-container" #leftDoc>
  <!-- 左侧文档内容 -->
</div>
<div class="document-container" #rightDoc>
  <!-- 右侧文档内容 -->
</div>

2. 组件逻辑

在组件中获取容器元素,监听滚动事件,基于映射表计算同步位置:

import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';
import { debounceTime, fromEvent } from 'rxjs';

@Component({
  selector: 'app-document-compare',
  templateUrl: './document-compare.component.html',
  styleUrls: ['./document-compare.component.css']
})
export class DocumentCompareComponent implements OnInit {
  @ViewChild('leftDoc') leftDoc!: ElementRef<HTMLDivElement>;
  @ViewChild('rightDoc') rightDoc!: ElementRef<HTMLDivElement>;

  // 预先生成的滚动映射表(实际项目中从差异对比逻辑获取)
  private scrollMap: ScrollMapItem[] = [];
  // 用于防止滚动事件循环触发的标记
  private isSyncing = false;

  ngOnInit(): void {
    this.initScrollListeners();
    // 这里调用你的差异对比逻辑生成scrollMap
    this.generateScrollMap();
  }

  private initScrollListeners(): void {
    // 左侧滚动监听
    fromEvent(this.leftDoc.nativeElement, 'scroll')
      .pipe(debounceTime(10)) // 防抖优化,减少计算频率
      .subscribe(() => {
        if (!this.isSyncing) {
          this.syncScroll('left');
        }
      });

    // 右侧滚动监听
    fromEvent(this.rightDoc.nativeElement, 'scroll')
      .pipe(debounceTime(10))
      .subscribe(() => {
        if (!this.isSyncing) {
          this.syncScroll('right');
        }
      });
  }

  private syncScroll(source: 'left' | 'right'): void {
    this.isSyncing = true;
    const sourceEl = source === 'left' ? this.leftDoc.nativeElement : this.rightDoc.nativeElement;
    const targetEl = source === 'left' ? this.rightDoc.nativeElement : this.leftDoc.nativeElement;
    const scrollTop = sourceEl.scrollTop;

    // 找到当前滚动位置对应的映射项
    const matchedItem = this.scrollMap.find(item => {
      if (source === 'left') {
        return scrollTop >= item.leftTop && scrollTop <= item.leftTop + item.leftHeight;
      } else {
        return scrollTop >= item.rightTop && scrollTop <= item.rightTop + item.rightHeight;
      }
    });

    if (matchedItem) {
      // 设置目标容器的滚动位置
      targetEl.scrollTop = source === 'left' ? matchedItem.rightTop : matchedItem.leftTop;
    } else {
      // 没有匹配项时,按比例滚动(作为 fallback)
      const sourceRatio = scrollTop / (sourceEl.scrollHeight - sourceEl.clientHeight);
      targetEl.scrollTop = sourceRatio * (targetEl.scrollHeight - targetEl.clientHeight);
    }

    // 重置标记,避免阻止后续滚动
    setTimeout(() => {
      this.isSyncing = false;
    }, 50);
  }

  private generateScrollMap(): void {
    // 这里替换成你的实际差异对比逻辑,生成scrollMap
    // 示例:假设左右文档有3个匹配块
    this.scrollMap = [
      { leftTop: 0, rightTop: 0, leftHeight: 150, rightHeight: 160 },
      { leftTop: 180, rightTop: 200, leftHeight: 200, rightHeight: 190 },
      { leftTop: 420, rightTop: 410, leftHeight: 120, rightHeight: 130 }
    ];
  }
}

interface ScrollMapItem {
  leftTop: number;
  rightTop: number;
  leftHeight: number;
  rightHeight: number;
}

三、优化要点

  • 防抖处理:用debounceTime减少滚动事件的计算次数,提升性能;
  • 避免循环触发:通过isSyncing标记,防止同步滚动时触发对方的滚动事件,形成循环;
  • 平滑滚动:可以给scrollTop设置添加过渡动画,比如在CSS中给.document-container添加scroll-behavior: smooth;;
  • 动态更新映射表:如果文档内容动态变化,要重新生成映射表。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 06:35:06