爱生活,爱编程,学习使我快乐
【问题】 canvas中画线条,线条效果比预期宽1像素且模糊。
【出现条件】 这种情况一般是垂直或者水平的线,且坐标为整数,宽度不是偶数。
【解决方法】 坐标偏移0.5像素。
查看源码对比下面的分析会更好理解哦。 canvas画线条源码
效果如下图(在PS中放大后效果)
【事例解析】
图中第3-6条上下并列的是【非整数宽】的线,从左到右宽分别是1.3px、0.8px、0.5px、0.1px。下面4条X坐标都偏移了0.5px。效果更接近预期的宽度。上面以整数为X坐标反而像是颜色淡点的2px宽的线。0.1px的更是看不到了。
从图中几根斜线发现,canvas画斜线毛边比较明显。但是越靠近45度角毛边会越少。(这个问题暂时没有找到好的解决方案,即使偏移0.5px也不行。只能尽量避免画斜线。)
图中下面第1、2条,分别是整数坐标和偏移0.5px坐标的5像素宽线。第一条实际效果看着像是6px。
图中下面第3、4条,分别是整数坐标和偏移0.5px坐标的宽为4像素的线。而这次反而是偏移0.5px的线宽度大了1px。这是为什么呢?下面“canvas画线的原理”会解释其中原因。
从图中下面的两个矩形(第一个是X,Y坐标均为整数,第二个是X,Y坐标均偏移了0.5)可以看出,垂直或者水平的线都会有这种问题。
下面我们看个例子
var dom = document.querySelector("#canvas1");
var ctx = dom.getContext('2d');
ctx.strokeStyle = '#000';
// 正常画线(坐标为整数,线宽为1px),1像素画出的效果像2像素。
ctx.lineWidth = 1;
ctx.moveTo(30, 50);
ctx.lineTo(30, 200);
ctx.stroke();
// 处理之后(坐标偏移0.5像素),线条宽度正常。
ctx.lineWidth = 1;
ctx.moveTo(50.5, 50);
ctx.lineTo(50.5, 200);
ctx.stroke();
效果如下图(在PS中放大后效果)
【实例解析】
1. 指定坐标为30px时,实际是以30px为中心向两边各画一半(0.5px),会画在30px前后的两个像素格子中。又因为像素是最小单位,所以30px前后的两个像素都被画了1px的线,但是颜色要比实际的谈一些。
ctx.strokeStyle = '#000';
ctx.lineWidth = 1;
// 默认从整数坐标画起时
ctx.beginPath();
ctx.strokeStyle = '#000';
ctx.lineWidth = 1.3;
ctx.moveTo(65, 50);
ctx.lineTo(65, 120);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = '#000';
ctx.lineWidth = 0.8;
ctx.moveTo(70, 50);
ctx.lineTo(70, 120);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = '#000';
ctx.lineWidth = 0.5;
ctx.moveTo(75, 50);
ctx.lineTo(75, 120);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = '#000';
ctx.lineWidth = 0.1;
ctx.moveTo(80, 50);
ctx.lineTo(80, 120);
ctx.stroke();
// 坐标偏移0.5px后
ctx.beginPath();
ctx.strokeStyle = '#000';
ctx.lineWidth = 1.3;
ctx.moveTo(65.5, 130);
ctx.lineTo(65.5, 200);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = '#000';
ctx.lineWidth = 0.8;
ctx.moveTo(70.5, 130);
ctx.lineTo(70.5, 200);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = '#000';
ctx.lineWidth = 0.5;
ctx.moveTo(75.5, 130);
ctx.lineTo(75.5, 200);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = '#000';
ctx.lineWidth = 0.1;
ctx.moveTo(80.5, 130);
ctx.lineTo(80.5, 200);
ctx.stroke();
效果如下图(在PS中放大后效果)
上图中,上面几条线是以整数为坐标的线,下面几条是坐标偏移了0.5px的线。
从该例子中看出,即使是非整数宽的线,坐标偏移0.5也能解决这种问题。当宽小于1px时,实际画的线还是1px宽,但是颜色要淡一些,视觉上就也达到了细一些的效果了(请看第一张图中的效果)。
以上所说的偏移0.5px,其实并不准确。因为上面例子中,坐标都是整数。
更准确的说法应该是:当线宽为偶数时,坐标应指定为整数。否则坐标应指定为整数+0.5px。
这里以竖线为例,横线同理
// 封装一个画线的方法
function drawLine (ctx, x, y1, y2, width) {
// 当线宽为偶数时,坐标应指定为整数。否则坐标应指定为整数+0.5px。
let newx = width % 2 === 0 ? Math.floor(x) : Math.floor(x) + 0.5;
ctx.lineWidth = width;
ctx.moveTo(newx, y1);
ctx.lineTo(newx, y2);
}
ctx.beginPath();
ctx.strokeStyle = '#000';
drawLine (ctx, 350, 250, 380, 1);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = '#000';
drawLine (ctx, 360, 250, 380, 2);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = '#000';
drawLine (ctx, 370.4, 250, 380, 1.3);
ctx.stroke();
具体效果请看canvas画线条源码中,右下角的三根线。