背景
给公司做过很多次的生成海报的H5活动,用的js库是html2canvas。在活动中发现了有些手机截图会比较模糊,查阅相关资料才知道,这跟手机的设备像素比(devicePixelRatio)有关系。
设备像素比(devicePixelRatio)
什么是设备像素比?我们先了解一下下面的表格内容。
概念 | 描述 | |
---|---|---|
像素 | px | 图像显示的基本单元,相对单位。 |
CSS像素 | - | 不考虑缩放情况下,1个CSS像素等于1个设备独立像素 |
设备像素(物理像素) | dp | 手机分辨率所描述的数字 |
设备独立像素(逻辑像素) | dip | 手机的实际视口大小 |
设备像素比 | dpr | devicePixelRatio,是物理像素和设备独立像素的比值 |
看到上图大概就能知道什么是设备像素比啦,所以当设备像素比为2的时候,CSS像素1px其实就代表了2个设备像素,所以当你设置了border是1px的时候,移动端的效果和设计稿的效果一对比你会发现手机上的border会更粗,于是让你苦恼的1px问题就出现了。
为什么有时候html2canvas截图会模糊
html2canvas的canvas模式其实就是利用canvas的各种方法实现的。现在大部分的手机都是高分屏,canvas中的单位1px,就是1物理像素,所以在高分屏下,canvas绘制的图片看起来就模糊了,通俗的说就是一张清晰度正常的普通图片为了布满整个背景被强行放大n倍,所以看起来模糊了。
如何解决
缩小画布内容为样式大小的devicePixelRatio倍,然后利用canvas的scale()方法进行放大devicePixelRatio倍。canvas的scale()方法在放大/缩小canvas时不会改变内容素质,也就是清晰度不会变。 举个例子来说,devicePixelRatio=3的设备下设置canvas的style.width为300,style.height为150,然后将canvas的width属性设置为style.width/devicePixelRatio=100,height属性设置为style.height/devicePixelRatio=50,因为画布大小由canvas的width和height属性决定的,所以缩放后画布的内容就是高清的了,但是这个时候画布内容是缩小的,这个时候利用sacle()进行放大devicePixelRatio=3倍可以了。
代码实现
// 高清截图
function generatePoster(dom) {
const domObj = document.getElementById(dom);
const canvas = document.createElement('canvas');
// 获取设备dpi
const dpi = devicePixelRatio > 1 ? devicePixelRatio : 1;
// 获取dom原宽高
const width = parseInt(domObj.offsetWidth, 10);
const height = parseInt(domObj.offsetHeight, 10);
// 调整画布大小
canvas.width = dpi * width;
canvas.height = dpi * height;
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
const ctx = canvas.getContext('2d');
// 放大画布
ctx.scale(dpi, dpi);
new html2canvas(domObj, {
canvas,
useCORS: true, // 跨域相关属性
allowTaint: true, // 跨域相关属性
backgroundColor: null,
scale: 1
}).then(canvas => {
let tempImg = new Image();
tempImg.style.width = width + 'px';
tempImg.style.height = height + 'px';
tempImg.src = canvas.toDataURL(); // 获取base64地址
tempImg.onload = function () {
console.log('生成成功');
};
return tempImg;
});
};