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

使用ngx-extended-pdf-viewer获取PDF页面Canvas并处理事件求助

解决ngx-extended-pdf-viewer中Canvas鼠标事件监听问题

针对你遇到的Canvas层级深无法监听鼠标事件的问题,推荐两种可靠的解决方案,基于ngx-extended-pdf-viewer的内置事件和事件委托机制实现:

方案一:事件委托到PDF查看器宿主元素

这种方式规避了Canvas动态重建(翻页、缩放时)导致的事件绑定失效问题,同时绕过DOM层级过深的限制:

1. 模板配置

给ngx-extended-pdf-viewer添加模板引用变量和afterRenderPage事件监听:

<ngx-extended-pdf-viewer #pdfViewer [src]="yourPdfUrl" (afterRenderPage)="onAfterRenderPage($event)"></ngx-extended-pdf-viewer>

2. 组件逻辑实现

import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';
import { AfterRenderPageEvent } from 'ngx-extended-pdf-viewer';

@Component({
  selector: 'app-your-pdf-component',
  templateUrl: './your-pdf-component.html',
  styleUrls: ['./your-pdf-component.css']
})
export class YourPdfComponent implements OnInit {
  @ViewChild('pdfViewer') pdfViewer!: ElementRef;
  private currentCanvas: HTMLCanvasElement | null = null;
  private ctx: CanvasRenderingContext2D | null = null;
  private isDrawing = false;
  private lastX = 0;
  private lastY = 0;

  ngOnInit(): void {
    // 将鼠标事件委托到PDF查看器的根元素
    const viewerRoot = this.pdfViewer.nativeElement;
    viewerRoot.addEventListener('mousedown', this.handleMouseDown.bind(this));
    viewerRoot.addEventListener('mousemove', this.handleMouseMove.bind(this));
    viewerRoot.addEventListener('mouseup', this.handleMouseUp.bind(this));
    viewerRoot.addEventListener('mouseleave', this.handleMouseUp.bind(this));
  }

  onAfterRenderPage(event: AfterRenderPageEvent) {
    // 每次页面渲染完成后,获取当前页面的Canvas
    this.currentCanvas = event.source;
    this.ctx = this.currentCanvas?.getContext('2d') || null;
    if (this.ctx) {
      this.ctx.strokeStyle = 'black';
      this.ctx.lineWidth = 2;
    }
  }

  private handleMouseDown(e: MouseEvent) {
    if (!this.currentCanvas || !this.ctx || e.target !== this.currentCanvas) return;
    
    this.isDrawing = true;
    // 计算鼠标在Canvas内的相对坐标
    const rect = this.currentCanvas.getBoundingClientRect();
    this.lastX = e.clientX - rect.left;
    this.lastY = e.clientY - rect.top;
  }

  private handleMouseMove(e: MouseEvent) {
    if (!this.isDrawing || !this.currentCanvas || !this.ctx) return;
    
    const rect = this.currentCanvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    
    // 绘制线条
    this.ctx.beginPath();
    this.ctx.moveTo(this.lastX, this.lastY);
    this.ctx.lineTo(x, y);
    this.ctx.stroke();
    
    this.lastX = x;
    this.lastY = y;
  }

  private handleMouseUp() {
    this.isDrawing = false;
  }
}

方案二:直接给Canvas绑定事件(需处理动态重建)

如果偏好直接给Canvas绑定事件,需要在每次页面渲染完成后重新绑定,避免DOM更新后事件失效:

onAfterRenderPage(event: AfterRenderPageEvent) {
  this.currentCanvas = event.source;
  this.ctx = this.currentCanvas?.getContext('2d') || null;
  if (this.ctx) {
    this.ctx.strokeStyle = 'black';
    this.ctx.lineWidth = 2;
  }

  // 先移除旧的事件绑定,防止重复触发
  this.currentCanvas.removeEventListener('mousedown', this.handleMouseDown.bind(this));
  this.currentCanvas.removeEventListener('mousemove', this.handleMouseMove.bind(this));
  this.currentCanvas.removeEventListener('mouseup', this.handleMouseUp.bind(this));
  this.currentCanvas.removeEventListener('mouseleave', this.handleMouseUp.bind(this));

  // 绑定新的事件
  this.currentCanvas.addEventListener('mousedown', this.handleMouseDown.bind(this));
  this.currentCanvas.addEventListener('mousemove', this.handleMouseMove.bind(this));
  this.currentCanvas.addEventListener('mouseup', this.handleMouseUp.bind(this));
  this.currentCanvas.addEventListener('mouseleave', this.handleMouseUp.bind(this));
}

关键说明

  • 优先使用afterRenderPage事件代替onPagesLoaded,因为前者会在**每次页面渲染(翻页、缩放、加载)**时触发,确保获取到最新的Canvas实例。
  • 事件委托的方式更稳定,无需担心Canvas动态重建导致的事件丢失问题。
  • 计算相对坐标时,必须通过getBoundingClientRect()获取Canvas的视口偏移,避免因页面滚动、缩放导致的坐标偏差。

内容的提问来源于stack exchange,提问作者Manuel Brás

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 13:43:29