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

Angular中绘制时HTML Canvas变黑问题求助

问题描述

我正在开发一款Angular应用,核心功能是让用户在PDF页面上绘制文本框。使用ng2-pdf-viewer库实现PDF预览,创建了包含PDF页面和对应HTML Canvas的组件。

遇到的问题:开始绘制时Canvas会变黑,虽然能成功画出矩形,但整个页面背景变成黑色,PDF内容被遮挡。

组件核心代码如下:

export class PdfPreviewPageComponent {
    @Input() pdfSrc: string = '';
    @Input() pdfPageNumber!: number;

    @ViewChild('pdfViewer', { static: false, read: ElementRef }) pdfViewerRef!: ElementRef;

    private canvas!: HTMLCanvasElement;
    private cx: CanvasRenderingContext2D | null = null;
    private isDrawing: boolean = false;
    private startX!: number;
    private startY!: number;

    // UPDATE: 改为使用ng2-pdf-viewer的(page-rendered)事件获取Canvas
    onPageLoaded(event: Event) {
        if (!this.canvas) {
            this.canvas = this._document.getElementsByTagName('canvas')[this.pdfPageNumber - 1];
            this.ctx = this.canvas.getContext('2d');

            if (this.ctx) {
                this.ctx.fillStyle = 'red';
                this.ctx.lineWidth = 2;
                this.handleCanvasEvent();
            }
        }
    }

    private handleCanvasEvent() {
        this.canvas.addEventListener('mousemove', (event) => this.onMouseMove(event));
        this.canvas.addEventListener('mousedown', (event) => this.onMouseDown(event));
        this.canvas.addEventListener('mouseup', (event) => this.onMouseUp(event));
    }

    private onMouseDown(event: MouseEvent) {
        this.isDrawing = true;
        this.startX = event.clientX - this.canvas.getBoundingClientRect().left;
        this.startY = event.clientY - this.canvas.getBoundingClientRect().top;
    }

    private onMouseMove(event: MouseEvent) {
        if (!this.isDrawing) {
            return;
        }

        const currentX = event.clientX - this.canvas.getBoundingClientRect().left;
        const currentY = event.clientY - this.canvas.getBoundingClientRect().top;
        const width = currentX - this.startX;
        const height = currentY - this.startY;

        this.clearCanvas();
        this.drawRectangle(this.startX, this.startY, width, height);
    }

    private onMouseUp(event: MouseEvent) {
        this.isDrawing = false;
    }

    private clearCanvas() {
        if (this.cx) {
            this.cx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        }
    }

    private drawRectangle(x: number, y: number, width: number, height: number) {
        if (this.cx) {
            this.cx.strokeRect(x, y, width, height);
        }
    }
    ...
    ...
}

核心需求:避免Canvas变黑,保留PDF内容,让用户能在PDF上绘制文本框。


解决方案

问题根源是你直接操作了ng2-pdf-viewer用来渲染PDF的Canvas——这个Canvas的像素数据就是PDF页面内容,调用clearRect会直接清除PDF的渲染结果,导致页面变黑。正确的做法是在PDF页面上方叠加一个独立的Canvas图层,所有绘制操作都在这个顶层Canvas上进行,完全不影响底层的PDF渲染。

步骤1:修改组件模板,添加独立Canvas

在pdf-viewer标签同级位置添加一个绝对定位的Canvas,确保它覆盖在PDF页面上方:

<div class="pdf-container" style="position: relative;">
  <pdf-viewer
    #pdfViewer
    [src]="pdfSrc"
    [page]="pdfPageNumber"
    (page-rendered)="onPageLoaded($event)"
    style="display: block;"
  ></pdf-viewer>
  <canvas
    #drawingCanvas
    style="position: absolute; top: 0; left: 0; pointer-events: none;"
  ></canvas>
</div>
  • pointer-events: none 确保Canvas不会阻挡PDF的基础交互(不需要的话可以移除)
  • 容器设置position: relative让Canvas能基于容器定位

步骤2:调整组件代码,绑定独立Canvas

修改组件代码,获取自定义的Canvas,而非PDF自带的Canvas:

export class PdfPreviewPageComponent {
    @Input() pdfSrc: string = '';
    @Input() pdfPageNumber!: number;

    @ViewChild('pdfViewer', { static: false, read: ElementRef }) pdfViewerRef!: ElementRef;
    // 获取自定义的绘制Canvas
    @ViewChild('drawingCanvas', { static: false, read: ElementRef }) drawingCanvasRef!: ElementRef;

    private canvas!: HTMLCanvasElement;
    private cx: CanvasRenderingContext2D | null = null;
    private isDrawing: boolean = false;
    private startX!: number;
    private startY!: number;

    onPageLoaded(event: any) {
        if (!this.canvas) {
            this.canvas = this.drawingCanvasRef.nativeElement;
            // 同步Canvas尺寸和PDF页面一致
            const pdfCanvas = event.source.canvas;
            this.canvas.width = pdfCanvas.width;
            this.canvas.height = pdfCanvas.height;
            // 移除pointer-events限制,让Canvas能接收鼠标事件
            this.canvas.style.pointerEvents = 'auto';
            
            this.cx = this.canvas.getContext('2d');
            if (this.cx) {
                this.cx.strokeStyle = 'red'; // 绘制边框用strokeStyle,纠正之前的fillStyle
                this.cx.lineWidth = 2;
                this.handleCanvasEvent();
            }
        }
    }

    // 其余事件处理、绘制逻辑保持不变,操作独立Canvas不会影响PDF渲染
    private handleCanvasEvent() {
        this.canvas.addEventListener('mousemove', (event) => this.onMouseMove(event));
        this.canvas.addEventListener('mousedown', (event) => this.onMouseDown(event));
        this.canvas.addEventListener('mouseup', (event) => this.onMouseUp(event));
    }

    private onMouseDown(event: MouseEvent) {
        this.isDrawing = true;
        const rect = this.canvas.getBoundingClientRect();
        this.startX = event.clientX - rect.left;
        this.startY = event.clientY - rect.top;
    }

    private onMouseMove(event: MouseEvent) {
        if (!this.isDrawing) {
            return;
        }
        const rect = this.canvas.getBoundingClientRect();
        const currentX = event.clientX - rect.left;
        const currentY = event.clientY - rect.top;
        const width = currentX - this.startX;
        const height = currentY - this.startY;

        this.clearCanvas();
        this.drawRectangle(this.startX, this.startY, width, height);
    }

    private onMouseUp(event: MouseEvent) {
        this.isDrawing = false;
    }

    private clearCanvas() {
        if (this.cx) {
            this.cx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        }
    }

    private drawRectangle(x: number, y: number, width: number, height: number) {
        if (this.cx) {
            this.cx.strokeRect(x, y, width, height);
        }
    }
}

关键修改点说明

  1. 独立Canvas图层:完全分离PDF渲染和用户绘制的Canvas,避免操作PDF的核心渲染数据
  2. 尺寸同步:在page-rendered事件中获取PDF页面的Canvas尺寸,同步到绘制Canvas,确保坐标精准匹配
  3. 样式调整:移除绘制Canvas的pointer-events: none,让它能接收鼠标事件;将fillStyle改为strokeStyle,符合绘制边框的需求

这样修改后,绘制的矩形会显示在PDF页面上方,清除Canvas时只会清除用户绘制的内容,不会影响底层的PDF显示,也就解决了页面变黑的问题。


内容的提问来源于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 21:12:51