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); } } }
关键修改点说明
- 独立Canvas图层:完全分离PDF渲染和用户绘制的Canvas,避免操作PDF的核心渲染数据
- 尺寸同步:在
page-rendered事件中获取PDF页面的Canvas尺寸,同步到绘制Canvas,确保坐标精准匹配 - 样式调整:移除绘制Canvas的
pointer-events: none,让它能接收鼠标事件;将fillStyle改为strokeStyle,符合绘制边框的需求
这样修改后,绘制的矩形会显示在PDF页面上方,清除Canvas时只会清除用户绘制的内容,不会影响底层的PDF显示,也就解决了页面变黑的问题。
内容的提问来源于stack exchange,提问作者Manuel Brás
相关产品推荐
相关产品推荐

