Canvas 线段厚度奇偶问题
有时候我们使用 lineWidth
设置线段宽度的时候会感觉不明显,尤其是奇数的时候
我们写一个例子
<!DOCTYPE html> <meta charset="utf-8"> <canvas id="canvas-1" width="300" height="150"> </canvas> <script> var c = document.getElementById("canvas-1"); var ctx = c.getContext("2d"); for (var i = 0; i < 10; i++){ ctx.lineWidth = 1+i; ctx.beginPath(); ctx.moveTo(5+i*14,5); ctx.lineTo(5+i*14,140); ctx.stroke(); } </script>
运行结果如下
最左边为 1.0
,从左往右每次都递增 1.0
我们可以看到,线宽为奇数的时候,非常不清晰
要理解为什么会和样,我们就不得不去了解 Canvas 是怎么画线的
Canvas 绘制线条的机制
想要获得精确的线条,必须对线条是如何描绘出来的有所理解
见下图,用网格来代表 canvas 的坐标格,每一格对应屏幕上一个像素点
在第一个图中,填充了 (2,1) 至 (5,5) 的矩形,整个区域的边界刚好落在像素边缘上,这样就可以得到的矩形有着清晰的边缘
如果想要绘制一条从 (3,1) 到 (3,5),宽度是 1.0 的线条,就会得到像第二幅图一样的结果 实际填充区域(深蓝色部分)仅仅延伸至路径两旁各一半像素
而这半个像素又会以近似的方式进行渲染,这意味着那些像素只是部分着色,结果就是以实际笔触颜色一半色调的颜色来填充整个区域(浅蓝和深蓝的部分)
这就是上例中为何宽度为 1.0 的线并不准确的原因
要解决这个问题,必须对路径施以更加精确的控制
已知粗 1.0 的线条会在路径两边各延伸半像素,那么像第三幅图那样绘制从 (3.5,1) 到 (3.5,5) 的线条,其边缘正好落在像素边界,填充出来就是准确的宽为 1.0 的线条
对于那些宽度为偶数的线条,每一边的像素数都是整数,那么想要其路径是落在像素点之间 (如那从 (3,1) 到 (3,5)) 而不是在像素点的中间。同样,注意到那个例子的垂直线条,其 Y 坐标刚好落在网格线上,如果不是的话,端点上同样会出现半渲染的像素点
虽然开始处理可缩放的 2D 图形时会有点小痛苦,但是及早注意到像素网格与路径位置之间的关系,可以确保图形在经过缩放或者其它任何变形后都可以保持看上去蛮好:线宽为 1.0 的垂线在放大 2 倍后,会变成清晰的线宽为 2.0,并且出现在它应该出现的位置上