HTML5 Canvas and Anti-aliasing
Solution 1
Anti-aliasing cannot be turned on or off, and is controlled by the browser.
Can I turn off antialiasing on an HTML <canvas> element?
Solution 2
You may translate canvas by half-pixel distance.
ctx.translate(0.5, 0.5);
Initially the canvas positioning point between the physical pixels.
Solution 3
It's now 2018, and we finally have cheap ways to do something around it...
Indeed, since the 2d context API now has a filter
property, and that this filter property can accept SVGFilters, we can build an SVGFilter that will keep only fully opaque pixels from our drawings, and thus eliminate the default anti-aliasing.
So it won't deactivate antialiasing per se, but provides a cheap way both in term of implementation and of performances to remove all semi-transparent pixels while drawing.
I am not really a specialist of SVGFilters, so there might be a better way of doing it, but for the example, I'll use a <feComponentTransfer>
node to grab only fully opaque pixels.
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);
}
<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>
And for the ones that don't like to append an <svg>
element in their DOM, you can also save it as an external svg file and set the filter
property to path/to/svg_file.svg#remove-alpha
.
Solution 4
I haven't needed to turn on anti-alias because it's on by default but I have needed to turn it off. And if it can be turned off it can also be turned on.
ctx.imageSmoothingEnabled = true;
I usually shut it off when I'm working on my canvas rpg so when I zoom in the images don't look blurry.
Solution 5
Here's a workaround that requires you to draw lines pixel by pixel, but will prevent anti aliasing.
// 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
}
}
Basically, you find the length of the line, and step by step traverse that line, rounding each position, and filling in a pixel.
Call it with
var context = cv.getContext("2d");
drawLineNoAliasing(context, 20,30,20,50); // line from (20,30) to (20,50)
KRouane
Updated on July 05, 2022Comments
-
KRouane almost 2 years
How to turn on the anti-aliasing on an canvas.
The following code doesn't draw a smooth line:
var context = mainCanv.getContext("2d"); if (context) { context.moveTo(0,0); context.lineTo(100,75); context.strokeStyle = "#df4b26"; context.lineWidth = 3; context.stroke(); }
-
Perry Monschau almost 11 yearsI don't think getAngle is necessary for drawing lines. All you need to do is divide the difference between the two points by the 'dist' and multiply that by 'i'. Am I wrong?
-
Overcode almost 11 yearsyes you're correct. I just used angles for clarity..? I guess
-
Gaurav over 10 yearsimageSmoothingEnabled applies to pattern fills and drawImage, it does not affect general anti-aliasing. whatwg.org/specs/web-apps/current-work/multipage/…
-
zachdyer about 10 yearsThat's not true. It can be turned on and off with ctx.imageSmoothingEnabled.
-
Gaurav about 10 yearsimageSmoothingEnabled applies to pattern fills and drawImage, it does not affect general anti-aliasing. whatwg.org/specs/web-apps/current-work/multipage/…
-
zachdyer about 10 yearsOh in that case maybe it's the video card that would handle that.
-
Richard about 10 yearsNote that if you clear your canvas using the method of setting the width to itself: canvas.width = canvas.width this will reset the transzlation matrix and you'll need to translate by half a pixel again. You can avoid this by using clearRect() instead.
-
Tim Hettler about 10 yearsThis is the only answer that actually solves OPs issue
-
Bjorn over 9 yearsIt clearly helps with straight lines, but diagonal lines still look pretty terrible.
-
steve over 8 yearsholy toledo you just saved me a ton of time! about a year ago i started work on a pixel based painting web app, and got frustrated with the inability to disable anti-aliasing. i think the only solution id found at the time ran so slow that it was hopeless, but yours runs amazingly fast and does exactly what i need it to! thank you!!!
-
kristianp about 7 yearsDrawing lines pixel by pixel shouldn't need calls to sin and cos. Use Bresenham's algorithm, it's a piece of graphics history: en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
-
Octopus about 7 yearsIt just moves where the aliasing occurs.
-
rococo almost 7 yearsNote that floating point pixel values greatly reduce performance, so if you are drawing frequently this may not be a good idea
-
jedierikb over 5 yearsLooking to do something similar - map translucent to opaque pixels... stackoverflow.com/questions/53755451/…
-
goat about 5 yearsTerrific answer - you even included a zoom to emphasize the difference!
-
Kit about 4 yearsThis solution worked perfectly for me, and for all angles. Edge and Chrome.
-
Jeremy Thille about 4 years"It's now 2018", not for Internet Explorer. And it's actually 2020 by the time of this comment, and still not for Internet Explorer. Unfortunately, this dinosaur is STILL used by many people and some projects STILL require to support it...
-
Kaiido about 4 years@JeremyThille so? Should we deprive the rest of the world from modern techs because some companies are still using IE? I also have to support it for some clients, we just made clear to them that they won't have the "optimal experience". There are other solutions in this page, pick one if it's really a must for your project.
-
Jeremy Thille about 4 yearsOh, absolutely. I am fully for dropping IE, this is my everyday battle. Your solution is good, but IE drags us all :(
-
Kaiido about 4 years@JeremyThille Yes but latest IE was released in 2013, so having my answer saying at the beginning "it's now 2018" shouldn't deceive the readers. Your comment felt like it did deceive you. I may misread it though.
-
Jeremy Thille about 4 yearsMy point is the same as yours. It's now 2018, and now 2020, we should be able to use modern methods and APIs, but... unfortunately it's not 2018 yet for many people who still use IE for some reason :( Many people still live in 2013. Thought of the day.
-
i336_ over 3 yearsFWIW the HTML5 canvas is not GPU accelerated
-
i336_ over 3 yearsJust for the record/out of curiosity, what version of IE was this talking about? 11? ...10?
-
Kaiido over 3 years@i336_ all versions. The thread is from 2020 and it's been said in it that the last release of IE was made in 2013.
-
i336_ over 3 yearsAh, I see. That logical structure there sailed right over my head, thanks :) - and wow, I just realized that means everything's in a time-warp to (as of right now) 8 years ago (and then with a bunch of inconsistencies on top). That's quite the legacy entrenchment... :(