问题描述
最近h5项目有个绘制圆形进度条的需求,用原生canvas绘制后发现在手机上显示比较模糊,效果不是很好,于是查了一下资料发现在canvas context中也存在一个类似浏览器属性devicePixelRatio的属性,叫作webkitBackingStorePixelRatio,该属性的值决定了浏览器在渲染canvas之前会用几个像素来来存储画布信息,如果手机屏幕的devicePixelRatio值越高,就会导致canvas绘制的图案要用更多的像素去绘制,跟图片放大就会变得模糊类似,所以我们需要根据不同的屏幕渲染比缩放canvas绘制的大小,即改变canvas的宽度跟高度。
第一种解决方案
简单粗暴,直接使用github上的一个库即可hidpi-canvas-polyfill1
2
3
4...
<script src=".../dist/hidpi-canvas.min.js"></script>
<script src=".../your-canvas-stuff.js"></script>
...
第二种解决方法
由于项目是用rem适配各种屏幕,而canvas的width属性不支持rem,我不能直接在canvas标签上使用rem设置画布大小,而上面的库是根据canvas标签属性width和height的大小进行适配,因此需要获取屏幕渲染比,在绘制画布时涉及到大小、坐标的时候都要乘以这个渲染比,具体如下(vue):
html:1
2<!-- canvas的宽度跟高度需要乘以像素比,style设置原来的宽度高度 -->
<canvas id="canvas" :width="300 * ratio" :height="300 * ratio" :style="{width: '300px', height: '300px'}"></canvas>
js部分: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
27...
data() {
return {
canvas: null,
ctx: null,
ratio: 1 // 像素比
}
}
mounted: {
this.canvas = document.getElementById(id)
this.ctx = this.canvas.getContext('2d')
this.ratio = this.getPixelRatio(this.ctx)
this.ctx.clearRect(0, 0, 100 * this.ratio, 100 * this.ratio) // 清理100*100大小的画布宽度跟高度都要乘以像素比
this.ctx.arc(50 * this.ratio, 50 * this.ratio, 10 * this.ratio, Math.PI, 2 * Math.PI) // 绘制弧度时圆心的x,y坐标,还有半径都要乘以像素比
},
methods: {
getPixelRatio (context) { // 获取屏幕像素比
let backingStore = context.backingStorePixelRatio ||
context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1
return (window.devicePixelRatio || 1) / backingStore
}
}
...
这样就可以了,实际上hidpi-canvas-polyfill也是利用这个原理,修改了canvas原型对象,使得fillRect, clearRect, strokeRect, moveTo, lineTo, arc等绘制图形的方法大小乘以像素比。
具体实现代码放在github上。