Canvas drawings, like lines, are blurry

HtmlCanvas

Html Problem Overview


I have a <div style="border:1px solid border;" /> and canvas, which is drawn using:

context.lineWidth = 1;
context.strokeStyle = "gray";

The drawing looks quite blurry (lineWidth less than one creates even worse picture), and nothing near to the div's border. Is it possible to get the same quality of drawing as HTML using canvas?

var ctx = document.getElementById("canvas").getContext("2d");
ctx.lineWidth = 1;
ctx.moveTo(2, 2);
ctx.lineTo(98, 2);
ctx.lineTo(98, 98);
ctx.lineTo(2, 98);
ctx.lineTo(2, 2);
ctx.stroke();

div {
  border: 1px solid black;
  width: 100px;
  height: 100px;
}
canvas, div {background-color: #F5F5F5;}
canvas {border: 1px solid white;display: block;}

<table>
<tr><td>Line on canvas:</td><td>1px border:</td></tr>
<tr><td><canvas id="canvas" width="100" height="100"/></td><td><div>&nbsp;</div></td></tr>
</table>

Html Solutions


Solution 1 - Html

I found that setting the canvas size in CSS caused my images to be displayed in a blurry manner.

Try this:

<canvas id="preview" width="640" height="260"></canvas>

as per my post: https://stackoverflow.com/questions/17854336/html-blurry-canvas-images/17854337#17854337

Solution 2 - Html

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

Solution 3 - Html

Even easier fix is to just use this:

context = canvas.context2d;    
context.translate(0.5, 0.5);

From here on out your coordinates should be adjusted by that 0.5 pixel.

Solution 4 - Html

I use a retina display and I found a solution that worked for me here.

Small recap :

First you need to set the size of your canvas twice as large as you want it, for example :

canvas = document.getElementById('myCanvas');
canvas.width = 200;
canvas.height = 200;

Then using CSS you set it to the desired size :

canvas.style.width = "100px";
canvas.style.height = "100px";

And finally you scale the drawing context by 2 :

const dpi = window.devicePixelRatio;
canvas.getContext('2d').scale(dpi, dpi);

Solution 5 - Html

The Mozilla website has example code for how to correct resolution in a canvas: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio

enter image description here

    </head>
    
    <body>
    	<div align = "center">
    		<canvas id = "canvas">
    
    		</canvas>
    
    	</div>
          <script type="text/JavaScript">
            var canvas = document.getElementById('canvas');

            var ctx = canvas.getContext('2d');

            // Set display size (css pixels).
            var size = 200;
            canvas.style.width = size + "px";
            canvas.style.height = size + "px";

            // Set actual size in memory (scaled to account for extra pixel density).
            var scale = window.devicePixelRatio; // Change to 1 on retina screens to see blurry canvas.
            canvas.width = size * scale;
            canvas.height = size * scale;

            // Normalize coordinate system to use css pixels.
            ctx.scale(scale, scale);

            ctx.fillStyle = "#bada55";
            ctx.fillRect(10, 10, 300, 300);
            ctx.fillStyle = "#ffffff";
            ctx.font = '18px Arial';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';

            var x = size / 2;
            var y = size / 2;

            var textString = "I love MDN";
            ctx.fillText(textString, x, y);
      </script>
    </body>
    </html>

Solution 6 - Html

Lines are blurred because the canvas virtual size is zoomed to its HTML element actual size. To overcome this issue you need to adjust canvas virtual size before drawing:

function Draw () {
	var e, surface;
	e = document.getElementById ("surface");
	/* Begin size adjusting. */
	e.width = e.offsetWidth;
	e.height = e.offsetHeight;
	/* End size adjusting. */
	surface = e.getContext ("2d");
	surface.strokeRect (10, 10, 20, 20);
}
window.onload = Draw ()

<!DOCTYPE html>
<html>
<head>
<title>Canvas size adjusting demo</title>
</head>
<body>
<canvas id="surface"></canvas>
</body>
</html>

HTML:

Solution 7 - Html

Ok, I've figured this out once and for all. You need to do two things:

  1. place any lines on 0.5 px. Refer to this, which provides a great explanation:

https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors#A_lineWidth_example

  1. There are essentially two heights and two widths associated with the canvas. There is the canvas height and width and then there is the css style height and width of the element. These need to be in sync.

To do this, you need to calculate the css height and width as:

 var myCanvasEl = document.getElementById('myCanvas');
 var ctx = myCanvasEl.getContext('2d');
 myCanvasEl.style.height = myCanvasEl.height / window.devicePixelRatio + "px";
 myCanvasEl.style.width = myCanvasEl.width / window.devicePixelRatio + "px";
 

where myCanvasEl.style.height and myCanvasEl.style.widthis the css styling height and width of the element, while myCanvasEl.height and myCanvasEl.width is the height and width of the canvas.

OLD ANSWER (superseded by above):

This is the best solution I've found in 2020. Notice I've multiplied the devicePixelRatio by 2:

 var size = 100;
 var scale = window.devicePixelRatio*2;
 context.width = size * scale;
 cartesian_001El.style.height = cartesian_001El.height / window.devicePixelRatio + "px";
 cartesian_001El.style.height = cartesian_001El.height / window.devicePixelRatio + "px";
 context.height = size * scale;
 context.scale(scale, scale);

Solution 8 - Html

To avoid this issue in animation I would like to share a small demo.

Basically I am checking increment values each time & jumping in a set of 1px by removing float values.

HTML:

<canvas id="canvas" width="600" height="600"></canvas>

CSS:

  html, body{
    height: 100%;
  }
  body{
    font-family: monaco, Consolas,"Lucida Console", monospace;
    background: #000;
  }

  canvas{
    position: fixed;
    top: 0;
    left: 0;
    transform: translateZ(0);
  }

JS:

  canvas = document.getElementById('canvas');
  ctx = canvas.getContext('2d');
		
  ctx.translate(0.5, 0.5);
  
  var i = 0;
  var iInc = 0.005;
  var range = 0.5;
  
  raf = window.requestAnimationFrame(draw);
		
  function draw() {
    var animInc = EasingFunctions.easeInQuad(i) * 250;
    ctx.clearRect(0, 0, 600, 600);
    ctx.save();
    ctx.beginPath();
    ctx.strokeStyle = '#fff';
    var rectInc = 10 + animInc;

    // Avoid Half Pixel
    rectIncFloat = rectInc % 1; // Getting decimal value.
    rectInc = rectInc - rectIncFloat; // Removing decimal.

    // console.log(rectInc);
    ctx.rect(rectInc, rectInc, 130, 60);
    ctx.stroke();
    ctx.closePath();

    ctx.font = "14px arial";
    ctx.fillStyle = '#fff';
    ctx.textAlign = 'center';
    ctx.fillText("MAIN BUTTON", 65.5 + rectInc, 35.5 + rectInc);

    i += iInc;

    if (i >= 1) {
      iInc = -iInc;
    }
    if (i <= 0) {
      iInc = Math.abs(iInc);
    }

    raf = window.requestAnimationFrame(draw);
  }


  // Easing
  EasingFunctions = {
    // no easing, no acceleration
    linear: function(t) {
      return t
    },
    // accelerating from zero velocity
    easeInQuad: function(t) {
      return t * t
    },
    // decelerating to zero velocity
    easeOutQuad: function(t) {
      return t * (2 - t)
    },
    // acceleration until halfway, then deceleration
    easeInOutQuad: function(t) {
      return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t
    },
    // accelerating from zero velocity 
    easeInCubic: function(t) {
      return t * t * t
    },
    // decelerating to zero velocity 
    easeOutCubic: function(t) {
      return (--t) * t * t + 1
    },
    // acceleration until halfway, then deceleration 
    easeInOutCubic: function(t) {
      return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
    },
    // accelerating from zero velocity 
    easeInQuart: function(t) {
      return t * t * t * t
    },
    // decelerating to zero velocity 
    easeOutQuart: function(t) {
      return 1 - (--t) * t * t * t
    },
    // acceleration until halfway, then deceleration
    easeInOutQuart: function(t) {
      return t < .5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t
    },
    // accelerating from zero velocity
    easeInQuint: function(t) {
      return t * t * t * t * t
    },
    // decelerating to zero velocity
    easeOutQuint: function(t) {
      return 1 + (--t) * t * t * t * t
    },
    // acceleration until halfway, then deceleration 
    easeInOutQuint: function(t) {
      return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t
    }
  }

Solution 9 - Html

Something else that nobody talked about here when images are scaled (which was my issue) is imageSmoothingEnabled.

> The imageSmoothingEnabled property of the CanvasRenderingContext2D interface, part of the Canvas API, determines whether scaled images are smoothed (true, default) or not (false). On getting the imageSmoothingEnabled property, the last value it was set to is returned. > > This property is useful for games and other apps that use pixel art. When enlarging images, the default resizing algorithm will blur the pixels. Set this property to false to retain the pixels' sharpness. > > https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingEnabled

To disable it, simply set the properity to false:

ctx.imageSmoothingEnabled = false;

Solution 10 - Html

A related issue could be that you're setting the <canvas>'s height and width from CSS or other sources. I'm guessing it scales the canvas and associated drawings. Setting the <canvas> size using the height and width property (either from the HTML tag or a JS script) resolved the error for me.

Solution 11 - Html

in order to get rid of the blurryness you need to set the size of the canvas in two manners: first withcanvas.width = yourwidthhere; and canvas.height = yourheighthere; second by setting the css attribute either by js or a stylesheet

Solution 12 - Html

HTML:

<canvas class="canvas_hangman"></canvas> 

JS:

function setUpCanvas(){
      canvas = document.getElementsByClassName("canvas_hangman")[0];
      ctx = canvas.getContext('2d');
      ctx.translate(0.5, 0.5);

      // Set display size (vw/vh).
      var sizeWidth = 80 * window.innerWidth / 100,
          sizeHeight = 100 * window.innerHeight / 100 || 766; 

      // console.log(sizeWidth, sizeHeight);
      //Setting the canvas site and width to be responsive 
      canvas.width = sizeWidth;
      canvas.height = sizeHeight;
      canvas.style.width = sizeWidth;
      canvas.style.height = sizeHeight;
 }
 window.onload = setUpCanvas();

This perfectly sets up your HTML canvas to draw on, in a responsive manner too:)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser572559View Question on Stackoverflow
Solution 1 - HtmlBen PretoriusView Answer on Stackoverflow
Solution 2 - HtmlMatt GreerView Answer on Stackoverflow
Solution 3 - HtmlrevgumView Answer on Stackoverflow
Solution 4 - HtmlLittleJoeView Answer on Stackoverflow
Solution 5 - HtmlNic ScozzaroView Answer on Stackoverflow
Solution 6 - HtmlR0burView Answer on Stackoverflow
Solution 7 - HtmlNormajeanView Answer on Stackoverflow
Solution 8 - HtmlImran BughioView Answer on Stackoverflow
Solution 9 - HtmlFabien SaView Answer on Stackoverflow
Solution 10 - HtmlkrishnakeshanView Answer on Stackoverflow
Solution 11 - HtmlJava WizardView Answer on Stackoverflow
Solution 12 - HtmlAdam O CeallaighView Answer on Stackoverflow