共计 1443 个字符,预计需要花费 4 分钟才能阅读完成。
引子
近期的工作中,是继 canvas 宽高问题 之后碰到的第二个问题。
- Origin
- My GitHub
显示模糊问题
在 PC 浏览器上显示时,没有发现明显的模糊,还可以接受。但在手机上就会有明显的模糊。这是示例,扫描访问二维码如下。
示例中,用 css 控制 canvas 的宽高,里面的图片展示效果不一致。查询资料,在 stackoverflow 中发现同样的问题,通过实际测试发现:
- canvas 元素自身的属性
width
和height
,决定了多少像素可以显示在画布上,如果不设置,width 默认值是 300,height 默认值是 150。 - css 的属性
width
和height
,是指在屏幕上元素显示的大小,如果没有对 canvas 进行 css 设置,则会采用 canvas 的默认大小。 - 如果设置了 css 属性
width
和height
,当在画布里面使用drawImage
设置图片宽高时,显示的宽高值会根据一定比例进行转换。例如在上面例子中,设置的图片是宽 300 高 90,canvas 默认宽高渲染像素 300 和 150,(css 高度 /canvas 自身属性高度) drawImage 设置的高度 =(90/150) 90 = 54。
在规范里面还真没看出来这些。
原因
在 stackoverflow 上也找到相关的问题,在回答中有相关介绍的文章 HTML5 Rocks。原因是 canvas 绘制时独立于设备像素比(devicePixelRatio)。受到 devicePixelRatio 影响,在高清显示屏上,一个逻辑像素对应多个实际的设备物理像素。例如在 devicePixelRatio 为 2 的设备上,css 设置的 100px,意味着设备上要填充 200px 物理像素,那么当 canvas 绘制 100px 的区域时,实际是想在设备填充 100px 物理像素,但由于 devicePixelRatio 的作用,设备要求显示 200px 的物理像素,浏览器就智能的填充了像素之间的空格,以便以适当的大小显示元素。
在 Safari6 中支持一个属性 backingStorePixelRatio,该属性决定了浏览器在渲染 canvas 会用几个像素来绘制画布的信息。有些类似于 devicePixelRatio。Safari6 的值为 2,所以在 Safari6 中 canvas 不会模糊,但后来去掉了,现在浏览器不支持这个属性,具体见 Issue 277205。
解决方法
解决的思路就是通过检测设备像素比,绘制对应倍数比例的 canvas 元素。方法如下:
function createHDCanvas (w=300,h=150) {
var ratio = window.devicePixelRatio || 1;
var canvas = document.createElement('canvas');
canvas.width = w * ratio; // 实际渲染像素
canvas.height = h * ratio; // 实际渲染像素
canvas.style.width = `${w}px`; // 控制显示大小
canvas.style.height = `${h}px`; // 控制显示大小
canvas.getContext('2d').setTransform(ratio, 0, 0, ratio, 0, 0);
return canvas;
}
这是对比示例,扫描访问二维码如下。
参考资料
- The canvas element
- How do I fix blurry text in my HTML5 canvas?
- Why context2d.backingStorePixelRatio deprecated?