Javascript 如何使线条的边缘像素保持半透明?
我正在使用一个HTML画布,其像素宽度和高度放大了32倍。然而,当我在上面画线时,我注意到线的边缘像素是半透明的(完全是半透明的)。有没有办法阻止这一切 在中,红线是从一点到另一点的单线。我希望所有的积木都是黑色或#FF0000红色 注意:我已经在使用canvas.translate()正确对齐像素,并且正在使用中的解决方案在离散块中渲染扩展的像素。问题背景 Canvas使用抗锯齿使图形看起来更平滑,这就是为什么它会到处填充半透明像素的原因(请参见了解其工作原理) 平滑(也称为插值)可以关闭,但仅适用于图像(Javascript 如何使线条的边缘像素保持半透明?,javascript,html,canvas,transparency,Javascript,Html,Canvas,Transparency,我正在使用一个HTML画布,其像素宽度和高度放大了32倍。然而,当我在上面画线时,我注意到线的边缘像素是半透明的(完全是半透明的)。有没有办法阻止这一切 在中,红线是从一点到另一点的单线。我希望所有的积木都是黑色或#FF0000红色 注意:我已经在使用canvas.translate()正确对齐像素,并且正在使用中的解决方案在离散块中渲染扩展的像素。问题背景 Canvas使用抗锯齿使图形看起来更平滑,这就是为什么它会到处填充半透明像素的原因(请参见了解其工作原理) 平滑(也称为插值)可以关闭,但
ctx.imageSmoothingEnabled=false,顾名思义)
解决
为此,需要实现“线渲染器”。然而,典型的线条算法只支持宽度为1像素的线条。这包括Bresenham和EFLA(极快直线算法),后者比Bresenham快
对于大于1像素的线,需要找到切线角度,然后沿主线渲染每个线段
我提供了以下两种实现,我在一定程度上对它们进行了优化。它们都不需要访问位图本身,只需提供上下文即可
唯一需要记住的是使用fillStyle
(和fill()
)而不是strokeStyle
(和stroke()
)来设置其颜色。可以在填充之前生成几行,这通常比填充每个线段快,前提是它们使用相同的颜色
(可选)您可以使用图像数据并直接在那里设置像素,但如果您使用图像,则速度较慢且需要CORS(如果首选,请使用带有Uint32视图的位图。还有一些特殊技巧可以加速此方法,但此处不讨论)
EFLA(极快直线算法)
此算法适用于要绘制连续多边形线的位置,即未设置最后一点。但是在下面的实现中,我们手动设置它,以便它可以用于单个线段
请访问上面的链接站点以获得更深入的解释(以及许可证)
只需确保输入值是整数值:
function lineEFLA(ctx, x1, y1, x2, y2) {
var dlt, mul, yl = false, i,
sl = y2 - y1,
ll = x2 - x1,
lls = ll >> 31,
sls = sl >> 31;
if ((sl ^ sls) - sls > (ll ^ lls) - lls) {
sl ^= ll;
ll ^= sl;
sl ^= ll;
yl = true;
}
dlt = ll < 0 ? -1 : 1;
mul = (ll === 0) ? sl : sl / ll;
if (yl) {
x1 += 0.5; // preset for rounding
for (i = 0; i !== ll; i += dlt) setPixel((x1 + i * mul)|0, y1 + i);
}
else {
y1 += 0.5;
for (i = 0; i !== ll; i += dlt) setPixel(x1 + i, (y1 + i * mul)|0);
}
setPixel(x2, y2); // sets last pixel
function setPixel(x, y) {ctx.rect(x, y, 1, 1)}
}
功能线FLA(ctx、x1、y1、x2、y2){
var dlt,mul,yl=假,i,
sl=y2-y1,
ll=x2-x1,
lls=ll>>31,
sls=sl>>31;
如果((sl^sls)-sls>(ll^lls)-lls){
sl^=ll;
ll^=sl;
sl^=ll;
yl=真;
}
dlt=ll<0?-1:1;
mul=(ll==0)?sl:sl/ll;
if(yl){
x1+=0.5;//四舍五入预设
对于(i=0;i!==ll;i+=dlt)setPixel((x1+i*mul)| 0,y1+i);
}
否则{
y1+=0.5;
对于(i=0;i!==ll;i+=dlt)设置像素(x1+i,(y1+i*mul)|0);
}
setPixel(x2,y2);//设置最后一个像素
函数setPixel(x,y){ctx.rect(x,y,1,1)}
}
布雷森汉姆
这是一种经典的线条算法,在过去需要渲染简单线条的许多应用程序和计算机中使用
对算法进行了更详细的解释
功能线Bresenham(ctx、x1、y1、x2、y2){
如果(x1==x2){//特殊情况,垂直线
ctx.rect(x1,数学最小值(y1,y2),1,数学绝对值(y2-y1)+1);
回来
}
如果(y1==y2){//特殊情况,水平线
ctx.rect(数学最小值(x1,x2),y1,数学绝对值(x2-x1)+1,1);
回来
}
var dx=Math.abs(x2-x1),sx=x1dy?dx:-dy)*0.5;
而(!0){
ctx.rect(x1,y1,1,1);
如果(x1==x2&&y1==y2)中断;
var e2=错误;
如果(e2>-dx){err-=dy;x1+=sx;}
如果(e2
现场演示,包括缩放
var ctx=document.querySelector(“canvas”).getContext(“2d”);
ctx.fillRect(0,0,ctx.canvas.width,ctx.canvas.height);//背景色
ctx.刻度(20,20);//规模
ctx.fillStyle=“#f00”//本例中线条的颜色
linefla(ctx,0,0,17,20);//算法1
lineBresenham(ctx,3,0,20,20);//算法2
ctx.fill();//填充矩形,下一步使用beginPath()
功能线FLA(ctx、x1、y1、x2、y2){
/*x1 |=0;//确保值为整数值
x2 |=0;
y1 |=0;
y2 |=0*/
var dlt,
穆尔,
sl=y2-y1,
ll=x2-x1,
yl=假,
lls=ll>>31,
sls=sl>>31,
我
如果((sl^sls)-sls>(ll^lls)-lls){
sl^=ll;
ll^=sl;
sl^=ll;
yl=真;
}
dlt=ll<0?-1:1;
mul=(ll==0)?sl:sl/ll;
if(yl){
x1+=0.5;
对于(i=0;i!==ll;i+=dlt)
设置像素((x1+i*mul)| 0,y1+i);
}否则{
y1+=0.5;
对于(i=0;i!==ll;i+=dlt)
设置像素(x1+i,(y1+i*mul)| 0);
}
setPixel(x2,y2);//设置最后一个像素
函数设置像素(x,y){
ctx.rect(x,y,1,1)
}
}
功能线Bresenham(ctx、x1、y1、x2、y2){
如果(x1==x2){//特殊情况,垂直线
ctx.rect(x1,数学最小值(y1,y2),1,数学绝对值(y2-y1)+1);
回来
}
如果(y1==y2){//特殊情况,水平线
ctx.rect(数学最小值(x1,x2),y1,数学绝对值(x2-x1)+1,1);
回来
}
var dx=数学绝对值(x2-x1),
sx=x1dy?dx:-dy)*0.5;
而(!0){
ctx.rect(x1,y1,1,1);
如果(x1==x2&&y1==y2)中断;
var e2=错误;
如果(e2>-dx){
err-=dy;
x1+=sx;
}
if(e2
问题背景
Canvas使用抗锯齿使图形显示为m
function lineBresenham(ctx, x1, y1, x2, y2) {
if (x1 === x2) { // special case, vertical line
ctx.rect(x1, Math.min(y1, y2), 1, Math.abs(y2 - y1) + 1);
return;
}
if (y1 === y2) { // special case, horizontal line
ctx.rect(Math.min(x1, x2), y1, Math.abs(x2 - x1) + 1, 1);
return;
}
var dx = Math.abs(x2 - x1), sx = x1 < x2 ? 1 : -1,
dy = Math.abs(y2 - y1), sy = y1 < y2 ? 1 : -1,
err = (dx > dy ? dx : -dy) * 0.5;
while(!0) {
ctx.rect(x1, y1, 1, 1);
if (x1 === x2 && y1 === y2) break;
var e2 = err;
if (e2 > -dx) { err -= dy; x1 += sx; }
if (e2 < dy) { err += dx; y1 += sy; }
}
}