HTML5 Canvas和Anti-aliasing

KRo*_*ane 55 html5 canvas antialiasing

如何在画布上打开消除锯齿功能.

以下代码未绘制平滑线:

var context = mainCanv.getContext("2d");
if (context) {
   context.moveTo(0,0);
   context.lineTo(100,75);

   context.strokeStyle = "#df4b26";
   context.lineWidth = 3;
   context.stroke();
}
Run Code Online (Sandbox Code Playgroud)

Bac*_*her 38

您可以按半像素距离翻译画布.

ctx.translate(0.5, 0.5);
Run Code Online (Sandbox Code Playgroud)

最初是画布在物理像素之间的定位点.

  • 它显然有助于直线,但对角线看起来仍然很糟糕. (5认同)
  • 请注意,如果您使用将宽度设置为自身的方法清除画布:canvas.width = canvas.width 这将重置转换矩阵,您将需要再次平移半个像素。您可以通过使用 clearRect() 来避免这种情况。 (2认同)
  • 它只是移动到发生混叠的位置。 (2认同)

Gau*_*rav 26

无法打开或关闭消除锯齿,并由浏览器控制.

我可以在HTML <canvas>元素上关闭抗锯齿吗?

  • imageSmoothingEnabled适用于图案填充和drawImage,它不会影响一般的抗锯齿.http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#image-smoothing (11认同)
  • 这不是真的.可以使用ctx.imageSmoothingEnabled打开和关闭它. (9认同)

zac*_*yer 22

我不需要打开反别名,因为它默认打开但我需要将其关闭.如果它可以关闭它也可以打开.

ctx.imageSmoothingEnabled = true;
Run Code Online (Sandbox Code Playgroud)

当我在我的画布RPG上工作时,我通常将其关闭,所以当我放大图像时看起来并不模糊.

  • imageSmoothingEnabled适用于图案填充和drawImage,它不会影响一般的抗锯齿.http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#image-smoothing (8认同)

Kai*_*ido 12

它现在是2018年,我们终于有了廉价的方法来做它周围的事情......

实际上,由于2d上下文API现在具有filter属性,并且此过滤器属性可以接受SVGF过滤器,我们可以构建一个SVGF 过滤器,它将仅保留我们图形中完全不透明的像素,从而消除默认的抗锯齿.

因此,它本身不会停用抗锯齿,但在实现方面和在绘制时移除所有半透明像素的性能方面都提供了一种廉价的方法.

我不是SVGFilters的专家,所以可能有更好的方法,但是对于这个例子,我将使用一个<feComponentTransfer>节点来抓取完全不透明的像素.

var ctx = canvas.getContext('2d');
ctx.fillStyle = '#ABEDBE';
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = 'black';
ctx.font = '14px sans-serif';
ctx.textAlign = 'center';

// first without filter
ctx.fillText('no filter', 60, 20);
drawArc();
drawTriangle();
// then with filter
ctx.setTransform(1, 0, 0, 1, 120, 0);
ctx.filter = 'url(#remove-alpha)';
// and do the same ops
ctx.fillText('no alpha', 60, 20);
drawArc();
drawTriangle();

// to remove the filter
ctx.filter = 'none';


function drawArc() {
  ctx.beginPath();
  ctx.arc(60, 80, 50, 0, Math.PI * 2);
  ctx.stroke();
}

function drawTriangle() {
  ctx.beginPath();
  ctx.moveTo(60, 150);
  ctx.lineTo(110, 230);
  ctx.lineTo(10, 230);
  ctx.closePath();
  ctx.stroke();
}
// unrelated
// simply to show a zoomed-in version
var zCtx = zoomed.getContext('2d');
zCtx.imageSmoothingEnabled = false;
canvas.onmousemove = function drawToZoommed(e) {
  var x = e.pageX - this.offsetLeft,
    y = e.pageY - this.offsetTop,
    w = this.width,
    h = this.height;
    
  zCtx.clearRect(0,0,w,h);
  zCtx.drawImage(this, x-w/6,y-h/6,w, h, 0,0,w*3, h*3);
}
Run Code Online (Sandbox Code Playgroud)
<svg width="0" height="0" style="position:absolute;z-index:-1;">
  <defs>
    <filter id="remove-alpha" x="0" y="0" width="100%" height="100%">
      <feComponentTransfer>
        <feFuncA type="discrete" tableValues="0 1"></feFuncA>
      </feComponentTransfer>
      </filter>
  </defs>
</svg>

<canvas id="canvas" width="250" height="250" ></canvas>
<canvas id="zoomed" width="250" height="250" ></canvas>
Run Code Online (Sandbox Code Playgroud)

对于那些不喜欢<svg>在DOM中附加元素的人,您也可以将其保存为外部svg文件并将filter属性设置为path/to/svg_file.svg#remove-alpha.

  • 很棒的答案-您甚至还添加了一个缩放来强调差异! (4认同)

Ove*_*ode 8

这是一种解决方法,需要您逐个像素地绘制线条,但会阻止抗锯齿.

// some helper functions
// finds the distance between points
function DBP(x1,y1,x2,y2) {
    return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
// finds the angle of (x,y) on a plane from the origin
function getAngle(x,y) { return Math.atan(y/(x==0?0.01:x))+(x<0?Math.PI:0); }
// the function
function drawLineNoAliasing(ctx, sx, sy, tx, ty) {
    var dist = DBP(sx,sy,tx,ty); // length of line
    var ang = getAngle(tx-sx,ty-sy); // angle of line
    for(var i=0;i<dist;i++) {
        // for each point along the line
        ctx.fillRect(Math.round(sx + Math.cos(ang)*i), // round for perfect pixels
                     Math.round(sy + Math.sin(ang)*i), // thus no aliasing
                     1,1); // fill in one pixel, 1x1
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上,您可以找到线条的长度,并逐步遍历该线条,围绕每个位置并填充像素.

叫它

var context = cv.getContext("2d");
drawLineNoAliasing(context, 20,30,20,50); // line from (20,30) to (20,50)
Run Code Online (Sandbox Code Playgroud)

  • 逐像素绘制线条不应该需要调用sin和cos.使用Bresenham的算法,它是一段图形历史:https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm (2认同)