Canvas Drawings, Like Lines, Are Blurry

Canvas drawings, like lines, are blurry

When drawing lines in canvas, you actually need to straddle the pixels. It was a bizarre choice in the API in my opinion, but easy to work with:

Instead of this:

context.moveTo(10, 0);
context.lineTo(10, 30);

Do this:

context.moveTo(10.5, 0);
context.lineTo(10.5, 30);

Dive into HTML5's canvas chapter talks about this nicely

HTML5 canvas stroke() thick and fuzzy

For some reason, your canvas is stretched. Because you have its css property width set to 100%, it is stretching it from some sort of native size. It's the difference between using the css width property and the width attribute on the <canvas> tag. You might want to try using a bit of javascript to make it fill the viewport (see jQuery .width()):

jQuery(document).ready(function(){
var canvas = document.getElementById('drawing');
canvas.width(($(window).width()).height($(window).height());
var context = canvas.getContext('2d');
//...

How do I fix blurry shape edges in HTML5 canvas?

I figured out what was wrong. It was the device-pixel-ratio property of the device. Anything other than a value of 1 would result in pixelated canvas content. Adjusting the zoom in a browser alters device-pixel-ratio, and some devices come with a high device-pixel-ratio such as retina display iPhones.

You have to account for this using Javascript. There is no other way. I wrote about this in more detail on my blog, and provide some other sources as well.

You can see the final result below.

Responsive canvas using vanilla JavaScript:

var aWrapper = document.getElementById("aWrapper");var canvas = document.getElementById("myCanvas");
//Accesses the 2D rendering context for our canvasdfdfvar ctx = canvas.getContext("2d");
function setCanvasScalingFactor() { return window.devicePixelRatio || 1;}
function resizeCanvas() { //Gets the devicePixelRatio var pixelRatio = setCanvasScalingFactor();
//The viewport is in portrait mode, so var width should be based off viewport WIDTH if (window.innerHeight > window.innerWidth) { //Makes the canvas 100% of the viewport width var width = Math.round(1.0 * window.innerWidth); } //The viewport is in landscape mode, so var width should be based off viewport HEIGHT else { //Makes the canvas 100% of the viewport height var width = Math.round(1.0 * window.innerHeight); }
//This is done in order to maintain the 1:1 aspect ratio, adjust as needed var height = width;
//This will be used to downscale the canvas element when devicePixelRatio > 1 aWrapper.style.width = width + "px"; aWrapper.style.height = height + "px";
canvas.width = width * pixelRatio; canvas.height = height * pixelRatio;}
var cascadeFactor = 255;var cascadeCoefficient = 1;
function draw() { //The number of color block columns and rows var columns = 5; var rows = 5; //The length of each square var length = Math.round(canvas.width/columns) - 2; //Increments or decrements cascadeFactor by 1, based on cascadeCoefficient cascadeFactor += cascadeCoefficient;
//Makes sure the canvas is clean at the beginning of a frame ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = columns; i >= 1; i--) { for (var j = rows; j >= 1; j--) { //Where the color magic happens ctx.fillStyle = "rgba(" + (j*i*(cascadeFactor-110)) + "," + (i*cascadeFactor) + "," + (j*cascadeFactor) + "," + 0.6 + ")"; ctx.fillRect((length*(i-1)) + ((i-1)*2), (length*(j-1)) + ((j-1)*2), length, length); } } if (cascadeFactor > 255 || cascadeFactor < 0) { //Resets the color cascade cascadeCoefficient = -cascadeCoefficient; } //Continuously calls draw() again until cancelled var aRequest = window.requestAnimationFrame(draw);}
window.addEventListener("resize", resizeCanvas, false);
resizeCanvas();draw();
#aWrapper {    /*Horizontally centers the canvas*/    margin: 0 auto;}
#myCanvas { /*This eliminates inconsistent rendering across browsers, canvas is supposed to be a block-level element across all browsers anyway*/ display: block;
/*myCanvas will inherit its CSS width and style property values from aWrapper*/ width: 100%; height: 100%;}asdfasdf
<div id="aWrapper">    <!--Include some fallback content on the 0.00001% chance your user's browser doesn't support canvas -->    <canvas id="myCanvas">Fallback content</canvas></div> 

Canvas element with blurred lines

You can oversample the canvas (fake double resolution):

Here's an illustration with standard resolution on top and "double" resolution on bottom:

A Demo: http://jsfiddle.net/m1erickson/M5NHN/

Sample Image

Html:

<canvas id="canvas1" width=300 height=150></canvas>
<canvas id="canvas2" width=600 height=300></canvas>

CSS:

#canvas1 {
border:1px solid red;
width:300px;
height:150px;
}
#canvas2 {
border:1px solid green;
width:300px;
height:150px;
}

JS:

var canvas = document.getElementById("canvas1");
var context1 = canvas.getContext("2d");
var canvas = document.getElementById("canvas2");
var context2 = canvas.getContext("2d");

draw(context1);

context2.scale(2, 2);
draw(context2);

function draw(c2){

c2.fillStyle = '#f00';
c2.beginPath();
c2.moveTo(0, 0);
c2.lineTo(100, 0);
c2.lineTo(80, 50);
c2.lineTo(0, 50);
c2.closePath();
c2.fill();

c2.fillStyle = "#000";
c2.beginPath();
c2.moveTo(0, 50);
c2.lineTo(80, 50);
c2.lineTo(60, 100);
c2.lineTo(0, 100);
c2.closePath();
c2.fill();
}

How come the canvas drawing is blurry, and how do I sharpen it?

Use image-rendering: pixelated; css property to turn off antialiasing

document.querySelectorAll('canvas').forEach(canvas => {
const ctx = canvas.getContext('2d')
ctx.fillStyle = 'magenta'
for (let i = 0; i < canvas.width; i += 1) {
ctx.fillRect(i, i, 1, 1)
}
})
canvas {
width: 100px;
height: 100px;
border: 1px solid black;
}

.pixelated {
image-rendering: pixelated;
}
<canvas width="10" height="10"></canvas>
<canvas class="pixelated" width="10" height="10"></canvas>

html5 canvas. blurry lines

The lines shown in the code are as crisp as they can get. Lines cannot be thinner than one pixel (this is what I get from your code in Firefox and Opera):

http://i.imgur.com/kY8imGi.png

You can from this point only give the illusion of them being thinner by reducing the blackness. For example, if you stroke with mid-gray (#777) color they will appear like this:

http://i.imgur.com/CDP4E3p.png

But they are the exact same size.

This illusion is the same that you would get if you used a line width of 0.5 or used a double sized canvas and reduced it to half using CSS (in those cases due to resampling / interpolation).

Addendum: If the lines in the images above still look uneven there is the possibility that your monitor settings aren't optimal, ie. the resolution in use does not match the physical pixels of the screen. Check that the system settings are using the recommended resolution as any other will force interpolation/resampling at system/hardware level and no matter what you do in canvas won't help.

function doStuff() {  var cnv = document.getElementById("cnvs");  var ctx = cnv.getContext("2d");  var lw = 1;  ctx.lineWidth = lw;  var xMax = cnv.width;  var ygMax = cnv.height;
var iTranslate = 0.5; // (lw % 2) / 2; ctx.translate(iTranslate, iTranslate);
deltaX = 12;
ctx.strokeStyle = "#777"; for (var x = deltaX; x < xMax; x += deltaX) { drawLine(ctx, x, ygMax, x, 0); }}
function drawLine(ctx, x0, y0, xf, yf) { ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(xf, yf); ctx.stroke();}
doStuff();
<canvas id="cnvs" width="200" height="100"></canvas>

Blurry effect using canvas

The solution requires (as noted by @Kaiido), the use of Window.devicePixelRatio:

The devicePixelRatio of Window interface returns the ratio of the resolution in physical pixels to the resolution in CSS pixels for the current display device. This value could also be interpreted as the ratio of pixel sizes: the size of one CSS pixel to the size of one physical pixel. In simpler terms, this tells the browser how many of the screen's actual pixels should be used to draw a single CSS pixel.

The code that solves the problem is:

if (devicePixelRatio != 1.0) {
context.canvas.style.width = (context.canvas.width / devicePixelRatio) + "px";
context.canvas.style.height = (context.canvas.height / devicePixelRatio) + "px";
}

This also works in browsers other than Chrome, They (in my own case) return the value 1 for Window.devicePixelRatio, leaving the previous functionality intact.



Related Topics



Leave a reply



Submit