简体   繁体   中英

How to undo canvas antialiasing?

I draw some rectangles and then erase and redraw them to simulate movement to the right. However canvas antialiasing makes them leave traces and I don't want to redraw the entire canvas. Here's what I'm talking about

And here is the code for that:

var canvas = document.getElementById("canvas");
canvas.width = 800;
canvas.height = 600;
var context = canvas.getContext("2d");
maxFps = 15;


function loop(x) {
    setTimeout(function() {
        undoRect(x);
        drawRect(x + 30);   
        loop(x + 30);
    }, 1000/maxFps);
};


function undoRect(x) {
    context.clearRect(x, 0, 30, 30);
};


function drawRect(x) {
    context.fillStyle = 'black';
    context.fillRect(x, 0, 30, 30);
};

loop(0);

So far I attempted to clear a bigger rectangle than what I'm drawing but that doesn't seem to work.

There is nothing wrong with the code shown. fillRect() and clearRect() does not need offsetting to avoid being anti-aliased.

The problem indicates that there has been applied an offset earlier in the program, or an current issue with the browser(s).

Make sure the transform is reset before running the loop:

ctx.setTransform(1,0,0,1,0,0);   // identity matrix
// start loop

If you still get issues then you should report this as a bug to Chromium/Mozilla, however as shown below, this is not an issue in more recent versions. You could also consider clearing bounding box +1 pixel each direction, optionally, clear whole canvas and redraw.

Here are the screen-recording results (click on image to see 100%) -

From Firefox (v47.0b9) :

火狐

From Chrome (v52 Canary) :

铬

No trails ( fiddle for test )

Here's why that's happening.

Here's how to fix it:

 var canvas = document.getElementById("canvas"); canvas.width = 800; canvas.height = 600; var context = canvas.getContext("2d"); maxFps = 15; function loop(x) { setTimeout(function() { undoRect(x); drawRect(x + 30); loop(x + 30); }, 1000 / maxFps); }; function undoRect(x) { context.clearRect(x, 0, 30, 30); }; function drawRect(x) { context.fillStyle = 'black'; context.fillRect(x, 0, 30, 30); }; loop(0.5); // boop 
 <canvas id="canvas"></canvas> 

Offset your rectangle by 0.5px .

Unfortunately there is no portable way to turn off anti-aliasing when drawing on canvas. This can create problems for example if you want to draw two semi-transparent polygons that share an edge as boundary pixels will not be handled correctly (there is no way to do antialiasing correctly when working on a per-primitive basis... perfectly correct antialiasing requires full-scene processing in the general case).

However if you only need to draw rectangles then you can get pixel-accurate results by using coordinates that for example are an int + 0.5 when drawing with a pen of size 1px.

When drawing filled rects with fillRect coordinates don't need adjustments:

 <!DOCTYPE html> <html> <body> <canvas id="canvas"></canvas> <script> var x = 0; setInterval(function() { var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); ctx.clearRect(x, 0, 30, 30); x += 10; ctx.fillStyle = "#F00"; ctx.fillRect(x, 0, 30, 30); }, 100); </script> </body> </html> 

Another option is to just use multiple overlapping canvases for animation instead of drawing/erasing on a single canvas (canvas pixels are transparent by default and per-primitive antialiasing on canvas correctly updates transparency on the edges).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM