h5 canvas绘制一像素曲线时,可能导致2px曲线的结果。本质是画笔线宽不能被2整除,导致像素不能正好填充,有不满1px像素的绘制区域,从而导致模糊、多出1px的问题。
请参考mdn上关于画笔线宽一节的描述:
https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors
这是由于绘制时,将画笔中线对齐坐标,必然有一半线宽在坐标之外,这一半的线宽,如果不是整数,会被自动加满,使之正好对齐一个像素点,由于本身是不满这1个像素的,所以用了更浅的颜色来表示(改变透明度),导致模糊。(因为1像素就是最小单位了,0.x的像素不能显示)
如:
当画笔线宽为1px,那么绘制直线L:(0,0),(100,0)时,画笔对齐 y 坐标 = 0。由于线宽的存在,实际绘制线条有一半线宽在y的负半轴。导致不可见。
那么线条矩形区域为:
Rect(left:0,top:-0.5,right:100;bottom:0.5)
0.5像素不可见,加满1像素,实际线条区域变为:
Rect(left:0,top:-1,right:100;bottom:1)
就变2px那么大了。
解决这个问题,我们只需要将线条的y坐标偏移0.5就行:
L:(0,0.5),(100,0.5)
当画笔中线对齐y坐标=0.5,时,线条矩形区域没有多余小数:
Rect(left:0,top:0,right:100,1)
这样,自然就是真的1px线条了。
综上,线宽为偶数不会产生误差,而线宽为小数,本来就无法准确绘制,不用处理了。
所以绘制时,我们只需要修正线宽为奇数的情况就行。甚至,只修复线宽1px的情况就可以,因为当线宽很大时,肉眼感知不到啊。
var fix1px = (cxt.lineWidth % 2 === 0) ?0: 0.5;
纵向线条,x坐标偏移:
x = x+fix1px ;
横向线条,y坐标偏移:
y = y+fix1px ;
有些场景下,或者直接偏移一半线宽。
1px线条变2px线条图示:
不满1像素时,像素加满1px,颜色透明度调整,导致线条变模糊图示:
此图是由画布保存下来的png图片,加上一个红色背景,放大到像素级的结果。
可以看到,内外边加满1px,且变得半透明。
更多清楚的描述在mdn上:
链接:https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors
手动找到线宽一节。
对比1px矩形,线段、3px矩形,3px矩形,可以发现1px、3px绘制时可能产生模糊、像素+1的问题。
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1024, height=768,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
<title>线宽引起的误差title>
head>
<body>
<canvas width="600" height="600" style="background-color: #fafafa">canvas>
<script>
var canvas = document.getElementsByTagName('canvas')[0];
var cxt = canvas.getContext('2d');
cxt.font = '14px 宋体'
cxt.lineWidth = 2;
cxt.beginPath();
cxt.moveTo(10,10)
cxt.lineTo(100,10);
cxt.closePath();
cxt.stroke();
cxt.fillText('2px,外边框也是2px,但因为线宽的一半在画布之外,被吃掉了,仅有1px可见',110,16);
cxt.lineWidth = 1;
cxt.strokeRect(0,0,canvas.width,canvas.height);//吃掉0.5px,1px已经是最小,采用颜色变浅来表示
cxt.strokeRect(30,30,100,100);//产生0.5像素溢出,追加满1px,导致边框==2px
cxt.fillText('边框==2px,且颜色变浅,模糊',50,65)
cxt.strokeRect(150.5,150.5,100,100)//对齐线宽中线,边框===1px
cxt.fillText('边框==1px,颜色正常',160,200)
cxt.lineWidth = 2;
cxt.strokeRect(300,300,100,100)
cxt.fillText('正常的2px边框',305,350)
cxt.lineWidth = 3;
cxt.strokeRect(450,450,100,100)
cxt.fillText('3px变4px,略微模糊',455,500)
cxt.lineWidth = 4;
cxt.strokeRect(300,450,100,100)
cxt.fillText('正常的4px',305,500)
cxt.lineWidth = 9;
cxt.strokeRect(20,450,100,100)
cxt.fillText('9px变10px, 只有内外边有一点点模糊',35,500)
cxt.lineWidth = 10;
cxt.strokeRect(20.5,300.5,100,100)
cxt.fillText('正常10px',35,355)
script>
body>
html>
可以看到3px变4px的矩形、9px变10px的矩形只有略微模糊,这是因为中间2像素是被实心填满颜色的,只有内外边各自补足到1px,所以比起1px变成2px来说,它模糊程度没有那么大。