origin

Fuzzy should is a classical problem in the canvas, believe you also have seen a lot of related articles, but always remember, because many concepts, description is not clear, so I just summed up an article, plane went to complex concepts, helping to drew a few sharp figure, to deepen understanding, I think the thief good 😄, remember to call me).

Reasons for ambiguity

In general, the reasons for ambiguity can be roughly divided into the following three points:

1. Canvas size is inconsistent with CSS size

Let’s get rid of the concept of CSS size and remember that all our drawing is done on the canvas size; After the canvas is drawn, the CSS size is related to the page rendering. Think of canvas as a fixed size photo and CSS as a container that will be covered regardless of CSS size (that’s how it works, no reason 😒). So if the length is the same as the width it will scale equally; If the ratio of length to width is different, it will stretch and deform; It’s just fine if it’s the same size. Like this:So in general we need to set canvas and CSS to be the same size:

<canvas width="600" height="600" style="width: 600px; height: 600px;"></canvas>
Copy the code

2. When drawing something less than 1px, it will automatically fill in 1px

You’ve probably heard the saying that if you draw a 1px vertical line at the point (100, 0), it’s going to feel fuzzy. (100, 0) is the center point of the line. When drawing, it will spread 1px/2=0.5px to both sides, rather than unilaterally. However, we cannot draw half a pixel, so the minimum is 1px, so it will automatically fill up to 1px (and the color is pure). So if you spread it out by 1px on each side you get a total of 2px. But only such lines should not be blurred, the top of the variable thick, no matter how complex the point of the graph will only show a jagged feeling. In fact, the main reason for the blur is that the color of the auto-complement part is not the same as before, but based on some algorithm (such as taking the approximate color of the surrounding) to generate. So why the color change? It is basically a means of smoothing to solve the sawtooth problem mentioned earlier. Don’t understand 😂? Look at the picture, which looks something like 👇🏻 :If you draw a 1px vertical line at (100.5, 0), it will not become thick or blurred. Translate (0.5, 0.5) will blur more or less at half a pixel. Therefore, when using canvas to draw, attention should be paid to reducing the value by half a pixel, or adding half a pixel when drawing, which is also optimization (including calculation and rounding). Check out the comparison below to get a feel for 👇🏻 :

3. Affected by HD screens

In fact, you’ll find that even though canvas is the same size as CSS, it can still be blurry due to the DPR (device pixel ratio, forget the other concepts 🐶), as shown below:Figure not understand 😂? Let’s take a look at the text explanation: Assuming that canvas and CSS are both 10 * 10 in size, there will be 100 points (pixels) in the picture drawn by canvas, i.e. only 100 points of information. However, in the HD screen (DPR = 2), we need 400 points of information. What if the original points are not enough? Then there is a set of algorithms that automatically generate information about these points, creating blurring. How should that do 🤔? We need more points, so we can do it like this, we make the canvas DPR times bigger, so we make the canvas DPR times bigger, and the CSS stays the same size, even though the canvas is bigger, But eventually when you draw on the page and put it in CSS, it will be back to its original size because of scaling (the reason mentioned in the first point above), and you will have more points. But what to pay attention to, the canvas becomes larger, the corresponding drawing operation (draw a circle, draw a rectangle, etc.) also needs to be enlarged accordingly, there are generally two methods, many articles only tell you to enlarge, but did not tell you how to do after the enlargement, so take a look at the following code should help you better understand 👇🏻 :

// Method 1: After zooming in on the canvas, directly use scale to zoom in on the entire coordinate system
// * But remember that we are always drawing in an enlarged coordinate system, and at some point (e.g., resetting the width and height of the canvas) the scale will be reset to 1, causing the image to be distorted
adaptDPR() { This method is called when the canvas is initialized
    const dpr = window.devicePixelRatio;
    const { width, height } = this.canvas;
    // Reset the canvas width and CSS size. Zoom the canvas; CSS stays the same because we need so many points
    this.canvas.width = Math.round(width * dpr);
    this.canvas.height = Math.round(height * dpr);
    this.canvas.style.width = width + 'px';
    this.canvas.style.height = height + 'px';
    // Scale the entire coordinate system directly, which is relative to each drawing operation
    this.ctx2d.scale(dpr, dpr);
    // Then draw as usual, such as a rectangle ctx2d. StrokeRect (x, y, w, h); I'm just going to do what I'm going to do, call it what I'm going to call it
}
Copy the code
// Method 2: After enlarging the canvas, you need to multiply each drawing API by DPR
// * This makes it very difficult to use, so we need to encapsulate all the drawing operations
/ / you can refer to the library: https://github.com/jondavidjohn/hidpi-canvas-polyfill, but the library is not covered all of the API
adaptDPR() { This method is called when the canvas is initialized
    const dpr = window.devicePixelRatio;
    const { width, height } = this.canvas;
    // Reset the canvas width and CSS size. Zoom the canvas; CSS stays the same because we need so many points
    this.canvas.width = Math.round(width * dpr);
    this.canvas.height = Math.round(height * dpr);
    this.canvas.style.width = width + 'px';
    this.canvas.style.height = height + 'px';
    // Note that scale is not used here
}
// Then multiply by DPR for each drawing API, such as rectangle drawing
strokeRect(x: number, y: number, w: number, h: number) {
    const { ctx2d, dpr } = this;
    if(! ctx2d)return;
    x = x * dpr;
    y = y * dpr;
    w = w * dpr;
    h = h * dpr;
    ctx2d.strokeRect(x, y, w, h);
}
Copy the code

Two methods are highly recommended to try, feel the difference, really want to try, or remember, the article this thing fleeting ☁️.

conclusion

Some students may still feel confused, so interested students can take a look at github to see how it is applied in the project, of course, there are accompanying article instructions (🔥 function curve drawing, vertical silk slide). If you have any questions, please like them and leave a comment. See you next time at 👊🏻.