画布上的画,就像线条一样,是模糊的

我有一个 <div style="border:1px solid border;" />和画布,它是用以下方法绘制的:

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

绘图看起来非常模糊(lineWidth 小于1会创建更糟糕的图片) ,而且没有接近 div 边界的内容。是否有可能获得相同的质量绘制作为 HTML 使用画布?

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>

79356 次浏览

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

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.

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: HTML Blurry Canvas Images

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
}
}

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);

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:

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

enter image description here

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);
<canvas id="canvas"></canvas>

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);

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

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 height 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, and in a responsive manner too :)

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;

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.

Here is my solution: set width and height for canvas

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

Also set in css, so it will not overflow from its parent

canvas {
width: 100%
height: 100%
}
canvas.width=canvas.clientWidth
canvas.height=canvas.clientHeight

Although LittleJoe's solution worked perfect on desktop it didn't work on mobile because on iphone 11 pro for example the dpi is 3 so I had to set width/height based on dpi. At the end it worked:

let width = 100, height = 100;
const dpi = window.devicePixelRatio;


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


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


canvas.getContext('2d').scale(dpi, dpi);