These are all the problems you will encounter when you have a preliminary understanding of canvas.

Example: Draw a rectangle on a canvas

html

<canvas id="myCanvas">Your browser does not support Canvas</canvas>
Copy the code

css:

#myCanvas{
            width: 400px;  height: 400px;
            border: 1px solid red;
        }
Copy the code

Js:

let c = document.getElementById("myCanvas")
console.log(c.width ,c.height)
 
let ctx = c.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(10.10.150.100);
Copy the code

This seemingly simple code opens the page and causes a problem:

1, didn’t I draw a rectangle 150 wide and 100 high? Why is the height longer than the width?

2, the coordinate is not x: 10, y: 10, why does it look like y is bigger than x? And the top and bottom look blurry

Deformation problem cause: style scaling

Read the official explanation before explaining why:

grid

Canvas Grid and coordinate space.

As shown, the Canvas element is covered by the grid by default. Generally speaking, a cell in a grid is equivalent to a pixel in a Canvas element.

The starting point of the grid is the upper-left corner (coordinate (0,0)). All elements are positioned relative to the origin. So the coordinates in the upper left corner of the blue square are X pixels from the left (X-axis) and Y pixels from the top (Y-axis) (coordinates are (X, Y)).

Note: Although the website says that a grid is equal to 1px, I don’t think it’s impossible to rule out multiple pixels in a grid on some devices. Similar to the 1px problem on mobile devices.

Canvas has only two attRs, width and height, in addition to the default property, which represent the pixels inside the canvas.

Such as:

<canvas id="myCanvas" width="400px" height="500px">Your browser does not support Canvas</canvas>
Copy the code

It means that this canvas has 400 pixels horizontally and 500 pixels vertically, which is what’s called a grid.

Canvas defaults to 300 by 150 pixels

The width and height set in the CSS style only indicates how the canvas will display. Like the normal DOM, it does not determine the pixels in the canvas. So when the style is 400 * 400 and the canvas defaults to 300 * 150, it will cause you to stretch or scale.

<style>
#myCanvas{
            width: 400px;  height: 400px;
            border: 1px solid red;
        }
</style>
<canvas id="myCanvas">Your browser does not support Canvas</canvas>
Copy the code

At this point, it is equivalent to the 300 * 150 display of things, according to the style scale, width about 1.33 times (400/300), height about 2.67 times (400/150). It can also be understood as the number of pixels remains unchanged, but the pixel density decreases with a larger area.

So we set the width of the rectangle to 150 and the height to 100, which is fine by itself, but what we see is the result of scaling with the style. We can do this if we add width and height to the canvas:

<canvas id="myCanvas"  width="400px" height="400px">Your browser does not support Canvas</canvas>
Copy the code

Effect:

1 px problem:

Mobile screens with different resolutions display differently at 1px. Canvas is similar to this problem.

Official examples:

If you wanted to draw a line from (3,1) to (3,5) with a width of 1.0, you would get the same result as in the second image. The actual filled area (dark blue) only extends to half the pixels on each side of the path. This half pixel is rendered in a similar way, meaning that those pixels are only partially colored, and the result is to fill the entire area (light blue and dark blue) with half the hue of the actual stroke color. This is why a line of 1.0 width is not accurate.

To solve this problem, you have to exercise more precise control over the path. Given that a line with a thickness of 1.0 extends half a pixel on each side of the path, draw lines from (3.5,1) to (3.5,5) as shown in the third figure, with the edges falling right at the pixel boundary, and fill them with an exact line with a width of 1.0.

Analysis:

  1. If only part of a pixel grid is rendered, the rest of the grid will be rendered approximately, so the lines we see will feel blurred.
  2. The exact coordinates can be determined by decimals, ensuring a 1px grid width.

Draw 1px in different ways:

Test environment: Chrome 95+

let ctx = c.getContext('2d');
// Draw a 1px rectangle
ctx.fillStyle = 'green';
ctx.fillRect(30.10.1.40);

// Draw a 1px line
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(40.10);
ctx.lineTo(40.50);
ctx.stroke();

ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(50.5.10);
ctx.lineTo(50.5.50);
ctx.stroke();
Copy the code

Use fillRect to draw 1px rectangles and set lineWidth to 1px line segments. Results:

It is obvious that the second line segment, although lineWidth is 1px, is wider than the rectangle with a width of 1px and is grey in color. This is the 1px problem mentioned above, 1px used to take up 1 grid, but ended up taking up 2, so it looks wider. And perhaps because of approximate rendering problems, the black line segment turns gray.

The third line segment moves the coordinates to the middle of the grid, ensuring that the rendering is done in one grid and the color is black by default.

Now let’s look at the other cases

Draw a 1px line segment with decimal coordinates:

Test environment: Chrome 95+

ctx.lineWidth = 1;
for (let i = 0; i < 10; i++) {
    ctx.beginPath();
    ctx.moveTo(50+i*10+i/10.10);
    ctx.lineTo(50+i*10+i/10.50);
    ctx.stroke();
}
Copy the code

Draw 10 lines with the coordinates increasing from 0 to 1, resulting in:

As you can see, when the decimal is 0.5, the width is 1px and it is black,

The others are colored from near to far based on the coordinates and are 2px

Coordinates are positive integers and lineWidth increases from 1-10:

Test environment: Chrome 95+

for (var i = 0; i < 10; i++) {
    ctx.lineWidth = 1 + i;
    ctx.beginPath();
    ctx.moveTo(20 + i * 14.10);
    ctx.lineTo(20 + i * 14.50);
    ctx.stroke();
}
Copy the code

You can see that lines with odd widths are not clear because odd numbers occupy one more pixel of the grid, but only render a portion of the grid.

Even lines are just enough to finish the grid.

Simple solution:

for (var i = 0; i < 10; i++) {
    ctx.lineWidth = 1 + i;
    ctx.beginPath();
    let x = 20 + i * 14
    if(ctx.lineWidth % 2! =0){
        ctx.moveTo(x + 0.5.10);
        ctx.lineTo(x + 0.5.50);
    }else{
        ctx.moveTo(x, 10);
        ctx.lineTo(x, 50);
    }
    
    ctx.stroke();
}
Copy the code

The result is clearly what we want:

Canvas Note one, over.