最近在使用 HTML Canvas 元素时发现通过 fillText() 绘制的文字明显发虚,像是以低分辨率渲染后放大所致,再仔细一检查,发现事实上包括 fillRect() 在内的其他图案也都存在相同的问题。
初步推测和显示设备使用了较高的 DPI 有关,随进行了一番搜索,在 Stackoverflow 、Medium 等站点都有一些提问和回答。然而意外的是,自己能找到的一些解答,至少在自己这边并不能解决问题。
经过摸索,找到了能够解决自己问题的办法,分享于此,以便后人。
以下为 Angular component 代码片段,语言为 TypeScript。
@ViewChild('canvas', { static: true }) canvas: ElementRef; // Canvas 元素 @ViewChild('canvasDiv', { static: true }) canvasDiv: ElementRef; // Canvas 元素的父元素,一个 div private ctx: CanvasRenderingContext2D; // Canvas context private dpiRatio: number; // DPI 缩放比例 private canvasDivWidth: number; // 父元素宽度
……
this.dpiRatio = window.devicePixelRatio; this.canvasDivWidth = this.canvasDiv.nativeElement.offsetWidth; // 分别设置 canvas 的宽度和样式宽度,canvas 的显示尺寸将会减半 this.ctx = this.canvas.nativeElement.getContext('2d'); this.ctx.canvas.width = this.canvasWidth * this.dpiRatio; this.ctx.canvas.style.width = this.canvasWidth + 'px'; // 在绘制时考虑缩放因素 this.ctx.font = 12 * this.dpiRatio + 'px Roboto'; this.ctx.fillText('HIDPI文字', x * this.dpiRatio, y * this.dpiRatio);
一个悬而未决的问题是,canvas context 本身的 transfer 函数似乎不能做到整体的放大,因此这里作为临时方案暂时在绘制每一个图形时都乘以了缩放倍率。这里应该有更好的办法,不过限于时间,暂且搁置。