HTML5:当我擦除图像时,抗锯齿会留下痕迹

时间:2012-12-15 21:12:03

标签: javascript html5 canvas sprite antialiasing

我想在画布上移动一个小部件,由于各种原因我不想使用精灵。我正在使用最新版本的Chrome。为了移动小部件,我'展开'它然后在另一个地方重绘它。通过'undraw',我的意思是我只是在同一个地方绘制相同的图像,但是用与背景相同的颜色绘制它,因此在我绘制新的之前,小部件完全消失。问题是,当我''undraw'时,原始图像的痕迹仍留在画布上。我在这里讨论了相关的问题,并没有找到任何有用的东西。我理解绘制一个像素线并获得抗锯齿的问题,所以我将线宽设置为2(以及各种其他非整数值),但无济于事。有人有主意吗?这是一个fiddle demo,这是执行更新的函数:

function draw(){
    if(previousX !== null) {
        ctx.lineWidth = 1;
        ctx.fillStyle = '#ffffff';
        ctx.strokeStyle = '#ffffff';
        drawCircle(previousX, previousY, 20);
    }

    ctx.lineWidth = 1;
    ctx.fillStyle = '#000000';
    ctx.strokeStyle = '#000000';
    drawCircle(x, y, 20);

        console.log('drew circle (' + x + ', ' + y + ')');

    previousX = x;
    previousY = y;
}

P.S。我只是一个没有丰富图形经验的业余爱好者,所以如果可能的话,请稍微愚弄你的答案。

1 个答案:

答案 0 :(得分:3)

当您绘制具有消除锯齿的形状时,您正在对某些像素进行实体覆盖,但仅覆盖边缘像素的部分覆盖。问题是像素(暂时忽略LCD面板)是不可分割的单位。那么我们如何部分覆盖像素呢?我们使用alpha通道实现了这一目标。

Alpha通道(和Alpha混合)将圆形边缘的颜色与其下方的颜色组合在一起。当圆圈仅部分覆盖像素时会发生这种情况。这是一个可视化此问题的快速图表。

enter image description here

通过在背景颜色中再次绘制圆圈,颜色混合会导致永久性变化无法消除。原因是:颜色混合再次发生,但这只会导致效果变软。

简而言之,重绘仅覆盖了总覆盖率的像素。边缘像素不完全是圆的一部分,因此无法掩盖边缘效应。

如果你需要删除圆圈,而是考虑恢复原来的圆圈。您可以复制原始内容,然后绘制圆圈,然后当您想要移动圆圈时,恢复原始内容并重复此过程。

This previous SO question可能会为您提供有关复制画布区域的一些想法。它使用drawImage方法。最好的解决方案是结合getImageData和putImageData方法。我modified your Fiddle example向您展示了如何做到这一点。您可以尝试以下代码:

var x, y, vx, vy;
var previousX = null, previousY = null;
var data = null;

function draw(){
    ctx.lineWidth = 2.5;
    ctx.fillStyle = '#000000';
    ctx.strokeStyle = '#FF0000';

    drawCircle(x, y, 20);

    previousX = x;
    previousY = y;
}

function drawCircle(x, y, r){
  // Step 3: Replace the stuff that was underneath the previous circle
  if (data != null)
  {
    ctx.putImageData(data, previousX - r-5, previousY - r-5);
  }

  // Step 1: Copy the region in which we intend to draw a circle
  data = ctx.getImageData(x - r-5, y - r-5, 2 * r + 10, 2 * r + 10);

  // Step 2: Draw the circle
  ctx.beginPath();
  ctx.arc(x, y, r, 0, Math.PI*2, true);
  ctx.closePath();
  ctx.stroke();
  ctx.fill();
}