HTML 5画布LineTo()线条颜色问题

3
我正在向一个HTML 5 2D画布绘制五条水平线:
var canvas_ctx = my_canvas.getContext("2d");
    canvas_ctx.lineWidth = 0.5;
    canvas_ctx.strokeStyle = "black";

    {
        let line_x = 0;
        let line_length = canvas_ctx.width;
        let offset = 5;
        let numLines = 5;
        let numYincrement = 10;
        for (let i=0;i<numLines * numYincrement;i+=numYincrement) {
            //canvas_ctx.beginPath();
            canvas_ctx.moveTo(line_x,i + offset);
            canvas_ctx.lineTo(line_length,i + offset);
            canvas_ctx.stroke();
            //canvas_ctx.closePath();
        }
    }

理想情况下,应该有5条黑色的线。但实际上,每一行的颜色似乎都会随着新行的添加而逐渐变淡(就像是一个渐变效果!),所以第五行是灰色的。如果我取消注释canvas_ctx.beginPath();canvas_ctx.closePath();,所有的线都会变成灰色。为什么会这样呢?

1个回答

13

笔画从坐标的两侧重叠。

var ctx = c.getContext('2d');
ctx.strokeStyle="red";
// draw big
ctx.scale(30, 30);
ctx.beginPath();
ctx.moveTo(5, 0);
ctx.lineTo(5, 10);
ctx.stroke();

drawPixelGrid();


function drawPixelGrid() {
  // simply renders where the pixel bounds are
  ctx.beginPath();
  // remove the zoom
  ctx.setTransform(1,0,0,1,0,0);
  ctx.strokeStyle = 'gray';
  ctx.lineWidth = 2; // avoid the problem we are demonstrating by using a perfect lineWidth ;-)

  for(let y=0; y<=300; y+=30) {
    ctx.moveTo(0, y);
    ctx.lineTo(300, y);
    for(let x=0; x<=300; x+=30) {
      ctx.moveTo(x, 0);
      ctx.lineTo(x, 300);
    }
  }
  ctx.stroke();
}
<canvas id="c" height=300></canvas>

但显然,一个像素不能同时设置为两种颜色。因此浏览器会应用反锯齿技术,它将淡化像素的颜色到另一种颜色,这是前景色和背景色混合的结果。因此,在白色或透明背景上使用黑色笔画会导致实际呈现灰色像素。这里我仍将红色作为示例:

var ctx = c.getContext('2d');
ctx.strokeStyle="red";
// first draw as on a 10*10 canvas
ctx.beginPath();
ctx.moveTo(5, 0);
ctx.lineTo(5, 10);
ctx.stroke();

// zoom it
ctx.imageSmoothingEnabled = 0;
ctx.globalCompositeOperation = 'copy';
ctx.drawImage(c, 0,0,9000,9000);

drawPixelGrid();

// this is not red...

function drawPixelGrid() {
  ctx.globalCompositeOperation = 'source-over';
  ctx.beginPath();
  ctx.setTransform(1,0,0,1,0,0);
  ctx.strokeStyle = 'gray';
  ctx.lineWidth = 2;

  for(let y=0; y<=300; y+=30) {
    ctx.moveTo(0, y);
    ctx.lineTo(300, y);
    for(let x=0; x<=300; x+=30) {
      ctx.moveTo(x, 0);
      ctx.lineTo(x, 300);
    }
  }
  ctx.stroke();
}
<canvas id="c" height=300></canvas>

避免此问题的一种方法通常是在您的坐标上应用偏移量,以便线条正确地延伸到像素边界。例如,对于1px lineWidth,您将应用0.5的偏移量:

var ctx = c.getContext('2d');
ctx.strokeStyle="red";
// first draw as on a 10*10 canvas
ctx.beginPath();
ctx.moveTo(5.5, 0); // offset +0.5px
ctx.lineTo(5.5, 10);
ctx.stroke();

// zoom it
ctx.imageSmoothingEnabled = 0;
ctx.globalCompositeOperation = 'copy';
ctx.drawImage(c, 0,0,9000,9000);

drawPixelGrid();
// now we've got a real red

function drawPixelGrid() {
  ctx.globalCompositeOperation = 'source-over';
  ctx.beginPath();
  ctx.setTransform(1,0,0,1,0,0);
  ctx.strokeStyle = 'gray';
  ctx.lineWidth = 2;

  for(let y=0; y<=300; y+=30) {
    ctx.moveTo(0, y);
    ctx.lineTo(300, y);
    for(let x=0; x<=300; x+=30) {
      ctx.moveTo(x, 0);
      ctx.lineTo(x, 300);
    }
  }
  ctx.stroke();
}
<canvas id="c" height=300></canvas>

但在您的情况下,您正在绘制0.5像素的线宽,因此没有偏移量能够消除此抗锯齿。

因此,如果您想要完美的颜色,请选择正确的线宽。


我很感谢你的详细回答,Kaiido! - shopofolive

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接