前言

Canvas 不是矢量图,而是像图片一样是位图模式的。如果不做 Retina 屏适配的话,例如二倍屏,浏览器就会以 2 个像素点的宽度来渲染一个像素,该 Canvas 在 Retina 屏幕下相当于占据了 2 倍的空间,相当于图片被放大了一倍,因此图片会变模糊。

现在的手机高清屏层出不穷,而且它们设备像素比 devicePixelRatio 现在不一定是 2 了,还有二点几的,说不定以后还有 3、4、5 等等的,所以要做一个通用的适配。

Canvas 渲染倍率

在 Canvas 下的 context 中也存在一个 webkitBackingStorePixelRatio 的属性,该属性的值决定了浏览器在渲染 Canvas 之前会用几个像素来来存储画布信息。在 iOS6 下的 safari 中的值是 2,但是在 chrome 和iOS7 的 safari 中的值却是 1。在 iOS6 下的 safari 中,如果有一张 100 × 100 像素的图片绘制,该图片首先会在内存中生成一张 200 × 200 的图片,然后再浏览器渲染时会按 100 × 100 的图片来显示,因此不会出现模糊失真的情况。而在在 chrome 和 iOS7 的 safari 中就会出现模糊。

但是 backingStorePixelRatio 属性在各浏览器厂商的获取方式不一样,所以需要加上浏览器前缀来实现兼容。

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const canvas = document.getElementById("canvas");
const context= canvas.getContext("2d");

// 屏幕的设备像素比
const devicePixelRatio = window.devicePixelRatio || 1;

// 浏览器在渲染canvas之前存储画布信息的像素比
const backingStoreRatio = context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;

// canvas的实际渲染倍率
const ratio = devicePixelRatio / backingStoreRatio;

按实际渲染倍率来缩放 Canvas

补充基础知识点如下:

  1. canvas.widthcanvas.height 用来设置画布大小;
  2. canvas.style.widthcanvas.style.height 是用来设置 Canvas 元素在 DOM 中的大小,可以控制元素的大小缩放。

例如:

1
<canvas width="640" height="800" style="width:320px; height:400px"></canvas>

就是 Canvas 画布大小为 640px * 800px,但是实际渲染到页面却是 320px * 400px,相当于缩小了一倍来显示。

因此可以总结出缩放方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function getRenderRatio (context) {
// 屏幕的设备像素比
const devicePixelRatio = window.devicePixelRatio || 1;

// 浏览器在渲染canvas之前存储画布信息的像素比
const backingStoreRatio = context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;

// canvas的实际渲染倍率
return devicePixelRatio / backingStoreRatio;
}

function init (canvas, width, height, ratio) {
canvas.width = width * ratio;
canvas.height = height * ratio;
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
}

const canvas = document.getElementById("canvas");
const context= canvas.getContext("2d");
const ratio = getRenderRatio(context);
init(canvas, 350, 400, ratio);

绘制注意事项

由于 Canvas 画布进行了缩放,因此在绘制文字、线条或者是图片的时候,也都要以相应的缩放比例来进行绘制,为避免所有的坐标点和宽高都进行缩放,我们可以使用 context.scale() 方法对后续的全部绘制比例进行统一的缩放,代码表示如下:

1
context.scale(ratio, ratio);

参考链接