解决canvas画图模糊

2,310 阅读2分钟

一、问题描述

在做一个H5项目的时候,有一个需求是需要将html生成图片供用户长按保存,最开始偷懒采用html2canvas,但是发现html2canvas也有不少坑,时不时出现保存图片不完整的情况,在尝试了很多方法之后,最终放弃了html2canvas, 决定直接在canvas上绘图。然而绘完图之后调用canvas.toDataURL()方法生成的图片非常模糊,严重影响用户体验。

最后通过查阅资料,解决了这个问题,在这里做个笔记,方便之后再使用canvas。

二、问题分析

在浏览器的window变量中有一个devicePixelRatio的属性,该属性决定了浏览器会用几个像素点来渲染1个像素,举例来说,假设某个屏幕的devicePixelRatio的值为2,一张100x100像素大小的图片,在此屏幕下,会用2个像素点的宽度去渲染图片的1个像素点,因此该图片在此屏幕上实际会占据200x200像素的空间,相当于图片被放大了一倍,因此图片会变得模糊。

类似的,在 canvas context中也存在一个backingStorePixelRatio的属性(仅Safari和Chrome),该属性的值决定了浏览器在渲染canvas之前会用几个像素来来存储画布信息。 backingStorePixelRatio 属性在各浏览器厂商的获取方式不一样,所以需要加上浏览器前缀来实现兼容。

三、如何解决

要做 Retina 屏适配,关键是知道当前屏幕的设备像素比,然后将 canvas 放大到该设备像素比来绘制,然后将 canvas 压缩到一倍来展示。

注意基础知识点:

  • 要设置canvas的画布大小,使用的是 canvas.width 和 canvas.height;
  • 要设置画布的实际渲染大小,使用的 style 属性或CSS设置的 width 和height,只是简单的对画布进行缩放。
// 例如2倍屏幕下示例代码:canvas的实际大小的750px × 1334px,但是实际渲染到页面的大小是375px × 667px,相当于缩小一倍来显示。
<canvas width="750" height="1334" style="width:375px; height:667px"></canvas>
// 1、获取 Canvas 对象
let canvas = document.getElementById('capture_canvas');
this.ctx = canvas.getContext('2d');
// 2、获取像素比,放大比例为:devicePixelRatio / webkitBackingStorePixelRatio , 以下是兼容的写法
let backingStore = this.ctx.backingStorePixelRatio ||
                   this.ctx.webkitBackingStorePixelRatio ||
                   this.ctx.mozBackingStorePixelRatio ||
                   this.ctx.msBackingStorePixelRatio ||
                   this.ctx.oBackingStorePixelRatio ||
                   this.ctx.backingStorePixelRatio || 1;
let ratio = (window.devicePixelRatio || 1) / backingStore;
// 3、将 Canvas 宽高进行放大,要设置canvas的画布大小,使用的是 canvas.width 和 canvas.height;
let oldWidth = canvas.width;
let oldHeight = canvas.height;
canvas.width = oldWidth * ratio;
canvas.height = oldHeight * ratio;
// 4、要设置画布的实际渲染大小,使用的 style 属性或CSS设置的 width 和 height,只是简单的对画布进行缩放。
canvas.style.width = oldWidth + 'px';
canvas.style.height = oldHeight + 'px';
// 5、放大倍数:由于 Canvas 放大后,相应的绘制图片时也要放大,可以直接使用 scale 方法
this.ctx.scale(ratio, ratio);